mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
953
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb3e772774 | ||
|
|
1c18e90b8a | ||
|
|
894b6f478d | ||
|
|
64dd4bdce1 | ||
|
|
299664d18b | ||
|
|
7c727ea818 | ||
|
|
88075a75b9 | ||
|
|
c02b484128 | ||
|
|
705bd8a385 | ||
|
|
52936d7eb5 | ||
|
|
4313162b5e | ||
|
|
0df3bbec24 | ||
|
|
225e2c21c3 | ||
|
|
3cc53acbf2 | ||
|
|
51fe894a3f | ||
|
|
6f7d8d7f62 | ||
|
|
f7633c9c3b | ||
|
|
5fa4711247 | ||
|
|
62096dc008 | ||
|
|
de8d0d4406 | ||
|
|
5bec7a2c75 | ||
|
|
a9b648b2a9 | ||
|
|
f6cfc80a1d | ||
|
|
6403048acd | ||
|
|
f203800dd9 | ||
|
|
58658f335a | ||
|
|
1ed3dee135 | ||
|
|
0a22292d1b | ||
|
|
8ab1600417 | ||
|
|
99710e7b74 | ||
|
|
0f11c249b9 | ||
|
|
f9b868b339 | ||
|
|
73918b137e | ||
|
|
5a74f351dc | ||
|
|
766b335410 | ||
|
|
4cd951ae41 | ||
|
|
0d64f06aa4 | ||
|
|
b32cb30737 | ||
|
|
ea2192a83c | ||
|
|
a7f1cc128a | ||
|
|
f6658c4f5a | ||
|
|
38b3333349 | ||
|
|
16b360e77e | ||
|
|
8b6529934d | ||
|
|
3d991ede3e | ||
|
|
62817f5ca7 | ||
|
|
45c8584903 | ||
|
|
16d65f32b3 | ||
|
|
481c0b9d7a | ||
|
|
d5bb5d4c0e | ||
|
|
0bbfcd3888 | ||
|
|
1b93c78c5f | ||
|
|
7a64987da7 | ||
|
|
c9e2be0e08 | ||
|
|
20800d760b | ||
|
|
801724e90e | ||
|
|
c3d6dcad86 | ||
|
|
8a5a1609fe | ||
|
|
408dcbc263 | ||
|
|
16a8fdce4e | ||
|
|
c247f4cc88 | ||
|
|
9c1f93d47b | ||
|
|
de06af6c4b | ||
|
|
8fea73d3bf | ||
|
|
290ebbfc33 | ||
|
|
a246ba1a9e | ||
|
|
4fa8f01ce2 | ||
|
|
244942dd4e | ||
|
|
ecfe962d43 | ||
|
|
212222ef74 | ||
|
|
b0f3a99b4b | ||
|
|
4c1d539e04 | ||
|
|
119834fc96 | ||
|
|
d33ecfa7f5 | ||
|
|
e0ebeb837a | ||
|
|
c7a0e26439 | ||
|
|
3b4fea655a | ||
|
|
8f4594babb | ||
|
|
e90df389dd | ||
|
|
348d748039 | ||
|
|
7c2b6d9330 | ||
|
|
09410bfe97 | ||
|
|
dc6e4159e9 | ||
|
|
7a507f00bd | ||
|
|
919bb9f4bd | ||
|
|
76c29f72f0 | ||
|
|
473f97fe92 | ||
|
|
18c25aa03a | ||
|
|
a040bf754e | ||
|
|
d1ab8c4df8 | ||
|
|
24283dff7f | ||
|
|
a63acb7f23 | ||
|
|
5524fdb7b6 | ||
|
|
786e9576ce | ||
|
|
d38383bf78 | ||
|
|
5287e70f9a | ||
|
|
a6a67301a4 | ||
|
|
1df1bf34eb | ||
|
|
325c9d61cc | ||
|
|
c8cbcfefbb | ||
|
|
6502c91877 | ||
|
|
8bc37026cd | ||
|
|
0c3fed9ef1 | ||
|
|
ba24b668c9 | ||
|
|
2a6572ec4a | ||
|
|
03dd5a1579 | ||
|
|
eea9e22923 | ||
|
|
be1cada848 | ||
|
|
61dba4eefb | ||
|
|
25650e7501 | ||
|
|
40266481e0 | ||
|
|
60e0554fdf | ||
|
|
c34f8afa81 | ||
|
|
ab5632bfcd | ||
|
|
7efa5b59f5 | ||
|
|
a49c3a33f0 | ||
|
|
3e9a24232c | ||
|
|
160b6c3386 | ||
|
|
d1729404c1 | ||
|
|
c06948b266 | ||
|
|
16835f96bf | ||
|
|
e0bd500aa2 | ||
|
|
de509c6928 | ||
|
|
6dab42e59a | ||
|
|
4590ae11ee | ||
|
|
6e1848f52f | ||
|
|
fa513d6242 | ||
|
|
424f24af04 | ||
|
|
e06b77cf39 | ||
|
|
a7c0ef3d09 | ||
|
|
e87a66c426 | ||
|
|
0a4b86214a | ||
|
|
78eb1eec25 | ||
|
|
ac5bc22817 | ||
|
|
6080a363ee | ||
|
|
3887ba69d7 | ||
|
|
d19f205207 | ||
|
|
36d110b809 | ||
|
|
34527efc79 | ||
|
|
d037ced578 | ||
|
|
5faea67631 | ||
|
|
ef6ed675b3 | ||
|
|
b96bd3bff6 | ||
|
|
04bc24f42b | ||
|
|
ec77ab1007 | ||
|
|
a6ceda3543 | ||
|
|
a142c5f094 | ||
|
|
490d8d435e | ||
|
|
671f801cd5 | ||
|
|
4f4edc48af | ||
|
|
48e57be312 | ||
|
|
886687ed34 | ||
|
|
a45db60313 | ||
|
|
a408cd3b57 | ||
|
|
c3b186b3f9 | ||
|
|
ff174238e8 | ||
|
|
bbe39299ac | ||
|
|
825c32eb17 | ||
|
|
12f8cf5c00 | ||
|
|
d290460202 | ||
|
|
36aa38c58e | ||
|
|
d4195aa3a6 | ||
|
|
5f3e986ed3 | ||
|
|
14cd43ddfd | ||
|
|
769a8ee6c9 | ||
|
|
6ce8c6a73d | ||
|
|
d89676bd3b | ||
|
|
5ec88389d6 | ||
|
|
3cfc062d8d | ||
|
|
7e2fde3a0a | ||
|
|
634a2a37c4 | ||
|
|
49a0095cd5 | ||
|
|
1abd096347 | ||
|
|
ac2fa382a7 | ||
|
|
32dbafa353 | ||
|
|
e1a772fd3e | ||
|
|
3cb27d7b2c | ||
|
|
d9946c399b | ||
|
|
4069ae7165 | ||
|
|
2af4ef705d | ||
|
|
1551de729b | ||
|
|
2703ca1106 | ||
|
|
c610c9aff1 | ||
|
|
5e8ef5523d | ||
|
|
579d3d7f14 | ||
|
|
d14edf4207 | ||
|
|
54f55f65f4 | ||
|
|
38a02a9e16 | ||
|
|
51152ae879 | ||
|
|
b8d84ccaaf | ||
|
|
80449c5c10 | ||
|
|
e6d8221c89 | ||
|
|
333682c4dc | ||
|
|
bb20b3f52c | ||
|
|
c0a489311c | ||
|
|
05bcb92f73 | ||
|
|
2b3cab887f | ||
|
|
d3e400a21e | ||
|
|
d31810d08b | ||
|
|
f236b8e09a | ||
|
|
6625d4e4a8 | ||
|
|
744cf4fbb7 | ||
|
|
032fec3cae | ||
|
|
e9a5299547 | ||
|
|
8e4ceebaef | ||
|
|
df06cd9139 | ||
|
|
36cfc4bee0 | ||
|
|
5f15c34ad5 | ||
|
|
0b6467291b | ||
|
|
2841875714 | ||
|
|
d5bf134c92 | ||
|
|
e627cab92e | ||
|
|
7389c9a5fe | ||
|
|
26f0efc34d | ||
|
|
a8bb54a33e | ||
|
|
d642111bfa | ||
|
|
e000234dbd | ||
|
|
2b08fb59c3 | ||
|
|
9f7d474455 | ||
|
|
6c2e407ffb | ||
|
|
d4b480b014 | ||
|
|
77ccec337e | ||
|
|
eb81474962 | ||
|
|
4eb971d3b6 | ||
|
|
c4a764d175 | ||
|
|
43c06c6b70 | ||
|
|
672c8b2730 | ||
|
|
b1cfd1c0ff | ||
|
|
226c3c0372 | ||
|
|
5c7e4393a7 | ||
|
|
17d0053f37 | ||
|
|
52700f12c2 | ||
|
|
2274767720 | ||
|
|
8eb69eb22e | ||
|
|
31e03bfaae | ||
|
|
cfc36a9f47 | ||
|
|
52e5947822 | ||
|
|
fdf73f8a7a | ||
|
|
1cf26b1f46 | ||
|
|
0ace58f00b | ||
|
|
fc1e54c046 | ||
|
|
3228ccb4f9 | ||
|
|
868c416202 | ||
|
|
7116405793 | ||
|
|
a9469e8734 | ||
|
|
71ffe2bf52 | ||
|
|
5f97aafe46 | ||
|
|
0753163066 | ||
|
|
186f84830a | ||
|
|
1788896a57 | ||
|
|
fdd39a29c3 | ||
|
|
bff846480a | ||
|
|
711dc7bd08 | ||
|
|
df76c7c268 | ||
|
|
8978442039 | ||
|
|
b52c9648f1 | ||
|
|
1a9bf23181 | ||
|
|
7c3ac9e014 | ||
|
|
a42a669d6e | ||
|
|
12d119069e | ||
|
|
d532c874d5 | ||
|
|
a2c1ce4e8f | ||
|
|
bd83351092 | ||
|
|
3ff6ccbda1 | ||
|
|
75d681e6e2 | ||
|
|
3121cd1c07 | ||
|
|
ab908aa9cb | ||
|
|
77aad0bfce | ||
|
|
831472fc64 | ||
|
|
6d8a969334 | ||
|
|
46bef5a61d | ||
|
|
0101fbda8c | ||
|
|
fb1c0c85b0 | ||
|
|
001d13d7e9 | ||
|
|
585ee4b88e | ||
|
|
3a68c77406 | ||
|
|
3fe031d223 | ||
|
|
abe145dae2 | ||
|
|
f2eb1c3b3e | ||
|
|
9b454b07b9 | ||
|
|
e8bd577d4e | ||
|
|
5c59083f4d | ||
|
|
9db6f4cbee | ||
|
|
00acc2f73b | ||
|
|
f9d0f8fc38 | ||
|
|
a309f31a33 | ||
|
|
a95cd51be0 | ||
|
|
f6e3759f6b | ||
|
|
097b342869 | ||
|
|
6f5b144e70 | ||
|
|
8213b37cf4 | ||
|
|
0eb0054f38 | ||
|
|
4d24c08220 | ||
|
|
c31e928def | ||
|
|
9b3f1a2c4a | ||
|
|
985a31ca04 | ||
|
|
8ae99cb373 | ||
|
|
f0485ca870 | ||
|
|
21a406830a | ||
|
|
5ffca0f6fa | ||
|
|
9f7bf5a9f7 | ||
|
|
5512340cdd | ||
|
|
f94f7763c8 | ||
|
|
d2aaa99076 | ||
|
|
ab19f1323e | ||
|
|
904e4c8650 | ||
|
|
df9e80a962 | ||
|
|
42ae429153 | ||
|
|
cefc7a1d4b | ||
|
|
2d102a122c | ||
|
|
41490602f7 | ||
|
|
8e13aafbc8 | ||
|
|
39490e9819 | ||
|
|
1509aa544d | ||
|
|
f24863349e | ||
|
|
e4ad2ed566 | ||
|
|
72ddef855e | ||
|
|
151cb78993 | ||
|
|
321227db15 | ||
|
|
b621ec2e8b | ||
|
|
778edf1ac2 | ||
|
|
24eef9accf | ||
|
|
f04e47b3d4 | ||
|
|
92ce8d711d | ||
|
|
19ea682a52 | ||
|
|
906ef59d95 | ||
|
|
410a35deb5 | ||
|
|
cb0416be7b | ||
|
|
65c0db3285 | ||
|
|
504711845a | ||
|
|
32657e22cc | ||
|
|
cb66c9073c | ||
|
|
964b616e05 | ||
|
|
24ecdd71b1 | ||
|
|
0702d7ec9c | ||
|
|
193f94e5e1 | ||
|
|
48d4d873de | ||
|
|
fe7f39d7a5 | ||
|
|
2517605cd1 | ||
|
|
9a0c72294c | ||
|
|
d6d7cf1d9f | ||
|
|
b519f5e23a | ||
|
|
af509f3479 | ||
|
|
496eb5bd12 | ||
|
|
979fcacd6c | ||
|
|
82687c8ca4 | ||
|
|
8c46f73f69 | ||
|
|
e406dead9a | ||
|
|
d633ad2bfb | ||
|
|
e0686894f1 | ||
|
|
b8fbbf3710 | ||
|
|
507f8c6955 | ||
|
|
d0f704480b | ||
|
|
4a29091ff4 | ||
|
|
156617d147 | ||
|
|
121b9f32b8 | ||
|
|
20fa8b3017 | ||
|
|
1581a8c654 | ||
|
|
a0c6ab3541 | ||
|
|
086e9bfab4 | ||
|
|
5030307715 | ||
|
|
bc068466c1 | ||
|
|
1aa99a0f3e | ||
|
|
b36fc1c2f8 | ||
|
|
bde1ad9b05 | ||
|
|
1f5ac2add0 | ||
|
|
f555939de6 | ||
|
|
1c8245de99 | ||
|
|
607d723037 | ||
|
|
8ce9e79cc1 | ||
|
|
af126a28ae | ||
|
|
dfeca259c5 | ||
|
|
a26f08a32b | ||
|
|
9c088f974a | ||
|
|
154993dafd | ||
|
|
638d79c326 | ||
|
|
4654c09edc | ||
|
|
4cbbd19cf9 | ||
|
|
25f82895c5 | ||
|
|
e1238d0fe9 | ||
|
|
0791b68149 | ||
|
|
b591eac230 | ||
|
|
f6d7a3d5cc | ||
|
|
3f48ec7e48 | ||
|
|
de95bf677e | ||
|
|
7d58879373 | ||
|
|
e246005c51 | ||
|
|
49cc99a556 | ||
|
|
c5dae9f018 | ||
|
|
a5516765b3 | ||
|
|
b3ea0bbb6b | ||
|
|
4112880330 | ||
|
|
7c4a954166 | ||
|
|
8ac2f64353 | ||
|
|
9680aae30f | ||
|
|
82df6bf936 | ||
|
|
c4991b9941 | ||
|
|
056a646c26 | ||
|
|
d1296dcc08 | ||
|
|
80247ddce9 | ||
|
|
a00c771be0 | ||
|
|
627ea74134 | ||
|
|
d25ac4998a | ||
|
|
ca9af0510a | ||
|
|
e387ab966f | ||
|
|
876bb6e2c8 | ||
|
|
707d9554df | ||
|
|
8a2083feb5 | ||
|
|
d82572acbe | ||
|
|
e744a7e022 | ||
|
|
5a8adcc0f6 | ||
|
|
8bc123848f | ||
|
|
f8c26e0f0a | ||
|
|
3571260a2b | ||
|
|
1848de14cc | ||
|
|
31cc4350a2 | ||
|
|
3ca7672726 | ||
|
|
1861ae3f68 | ||
|
|
16bed9de22 | ||
|
|
9febb06052 | ||
|
|
c7dd8574e4 | ||
|
|
36486dd3f4 | ||
|
|
828928b609 | ||
|
|
de8e82926a | ||
|
|
ef1380bf61 | ||
|
|
923252ea05 | ||
|
|
7e303399c7 | ||
|
|
453ce461f9 | ||
|
|
70c231dc5c | ||
|
|
f86b75733e | ||
|
|
b0bc888e6c | ||
|
|
c4b742ac0d | ||
|
|
fe62cf857c | ||
|
|
5511e3d0ec | ||
|
|
a6d2fe6d39 | ||
|
|
cad78159ea | ||
|
|
af7bd705f4 | ||
|
|
8160e39154 | ||
|
|
20cf7643dc | ||
|
|
3aa2bd6aa3 | ||
|
|
da4c4184bc | ||
|
|
b3ada0b017 | ||
|
|
b1769ed5ca | ||
|
|
b390485dd9 | ||
|
|
b200e66bd1 | ||
|
|
ff1d64a36a | ||
|
|
e73053d5b2 | ||
|
|
d0cc9c8a13 | ||
|
|
5622d1fd89 | ||
|
|
0d41b084ea | ||
|
|
48264673da | ||
|
|
9e525366cb | ||
|
|
557eefe2c4 | ||
|
|
0533792b00 | ||
|
|
730cb5280d | ||
|
|
d8c15079b6 | ||
|
|
439e42e1ab | ||
|
|
8e145a9519 | ||
|
|
0ecc61fe9e | ||
|
|
41bffa0165 | ||
|
|
fbb23b227b | ||
|
|
5d70884e6c | ||
|
|
3af2ea0d4c | ||
|
|
60b3b3a061 | ||
|
|
3cef3a76ce | ||
|
|
52bda3945d | ||
|
|
6f55068838 | ||
|
|
c5b93ce231 | ||
|
|
a008c7c67b | ||
|
|
626b2390e4 | ||
|
|
745968ebd4 | ||
|
|
9891f61610 | ||
|
|
1ce6a85026 | ||
|
|
4ae5e82d6f | ||
|
|
fbe824af10 | ||
|
|
583bb7d512 | ||
|
|
3f99f01953 | ||
|
|
30a69641cb | ||
|
|
a28f19ff3c | ||
|
|
b30e5d4cda | ||
|
|
d930fed475 | ||
|
|
70bf8e2c41 | ||
|
|
57c5925526 | ||
|
|
d39729d830 | ||
|
|
df7fa7c344 | ||
|
|
cd40df8388 | ||
|
|
24746de9ba | ||
|
|
f9992c2f64 | ||
|
|
59453873b7 | ||
|
|
3075cbe626 | ||
|
|
d335aee549 | ||
|
|
b2359786e6 | ||
|
|
72d720f83e | ||
|
|
2a3ae69f1c | ||
|
|
7dc64c12a6 | ||
|
|
59760bf529 | ||
|
|
501e7c2fa0 | ||
|
|
c58fa78962 | ||
|
|
42b85fad54 | ||
|
|
946bf20dd7 | ||
|
|
b263ed7ce6 | ||
|
|
e0a30fd25a | ||
|
|
7c1c025f9e | ||
|
|
5ae124c54c | ||
|
|
f3ab0d2dca | ||
|
|
74a8c7da12 | ||
|
|
ec4af20d09 | ||
|
|
feb775d2c4 | ||
|
|
0fcaaf99da | ||
|
|
e11cbd222e | ||
|
|
d29938107c | ||
|
|
e90b1d219e | ||
|
|
72d6fb3901 | ||
|
|
e79e2857f9 | ||
|
|
db269d8b8f | ||
|
|
fdc69e77f4 | ||
|
|
bad70b8257 | ||
|
|
c439fb08fd | ||
|
|
cb537534e3 | ||
|
|
d633c3e278 | ||
|
|
555f3f9406 | ||
|
|
a89cf443aa | ||
|
|
269efbe2f0 | ||
|
|
107fc1d069 | ||
|
|
1752c6be44 | ||
|
|
1f83dab9e1 | ||
|
|
bc5ac28b84 | ||
|
|
413f3c20ab | ||
|
|
afdf34c45c | ||
|
|
cedf3cf28d | ||
|
|
686b5439a2 | ||
|
|
7d6eac4ddb | ||
|
|
0e2679f07b | ||
|
|
c9bd16a353 | ||
|
|
08b539ba05 | ||
|
|
4e780c0f75 | ||
|
|
6ff1758873 | ||
|
|
31c21a6a88 | ||
|
|
2bf5809505 | ||
|
|
f16638503a | ||
|
|
4023613bb7 | ||
|
|
89aceb1db5 | ||
|
|
6854451565 | ||
|
|
2c2e88f645 | ||
|
|
7aacaad94d | ||
|
|
550a479532 | ||
|
|
982ccf8873 | ||
|
|
850e4c420b | ||
|
|
74c447877f | ||
|
|
d356d52c05 | ||
|
|
f3878e0f8b | ||
|
|
e17f391f25 | ||
|
|
a93fa2cb0c | ||
|
|
964be49c3a | ||
|
|
6bd83c8a70 | ||
|
|
98dee5f70d | ||
|
|
a2ce65a5ea | ||
|
|
a8b5a8a038 | ||
|
|
87e12d028d | ||
|
|
cdfc6e640a | ||
|
|
209c610cbe | ||
|
|
ff702d774f | ||
|
|
79fd72ead4 | ||
|
|
5820787afc | ||
|
|
627cbe285f | ||
|
|
38bc2bb925 | ||
|
|
36c9749098 | ||
|
|
40b1c28db0 | ||
|
|
373144e229 | ||
|
|
c4d64a52d6 | ||
|
|
5374336048 | ||
|
|
fc630bf3da | ||
|
|
6fb104016f | ||
|
|
f3a5572b59 | ||
|
|
c868c669ca | ||
|
|
2d03ce598b | ||
|
|
8cf96ff551 | ||
|
|
5c58ef2691 | ||
|
|
3c94e07bbb | ||
|
|
b1c047df47 | ||
|
|
78be575048 | ||
|
|
3ef150ff08 | ||
|
|
d13b5ca57e | ||
|
|
8e07ea2c69 | ||
|
|
f8c281dfc0 | ||
|
|
53a86e62d2 | ||
|
|
fec30cc614 | ||
|
|
9553dc0a85 | ||
|
|
2ccf617dd8 | ||
|
|
607c5c7638 | ||
|
|
a02a092faf | ||
|
|
a2030c170b | ||
|
|
8aebad1c01 | ||
|
|
588475cddf | ||
|
|
e78767f1a0 | ||
|
|
1ec3215900 | ||
|
|
e8125b4070 | ||
|
|
d2c9ce65e3 | ||
|
|
4f8e6d7005 | ||
|
|
03031805a9 | ||
|
|
d8148c5491 | ||
|
|
6b1196fb26 | ||
|
|
e11e922587 | ||
|
|
3d3248cbbb | ||
|
|
b35f906384 | ||
|
|
732a8938ce | ||
|
|
5b4444804b | ||
|
|
b215ab07fc | ||
|
|
853bd9a67c | ||
|
|
6208d78146 | ||
|
|
14dedb9392 | ||
|
|
6ef600deb9 | ||
|
|
c12854b369 | ||
|
|
5cdb8388a1 | ||
|
|
42631aa85e | ||
|
|
18664a73b2 | ||
|
|
ec1c78894c | ||
|
|
ed0652f86a | ||
|
|
2fc483b9e0 | ||
|
|
b0e218cb3b | ||
|
|
404ad5b75d | ||
|
|
cf6110530a | ||
|
|
cf7e1121fa | ||
|
|
9419e36de0 | ||
|
|
1cdac28631 | ||
|
|
43e7c1e9a6 | ||
|
|
fd52a172a7 | ||
|
|
4b0c782891 | ||
|
|
c43177093e | ||
|
|
5f27d75c11 | ||
|
|
f1da0b2e61 | ||
|
|
90c2bb10ff | ||
|
|
04181ec9ce | ||
|
|
14b40ace27 | ||
|
|
40e060bec4 | ||
|
|
867d814d47 | ||
|
|
9301bddd8b | ||
|
|
2491f5c70c | ||
|
|
cd70d74233 | ||
|
|
d2c7be19d0 | ||
|
|
1ca4904132 | ||
|
|
129c1e7c4f | ||
|
|
cb374eb9f6 | ||
|
|
2d84816bc9 | ||
|
|
ff1a679c49 | ||
|
|
f3874e680b | ||
|
|
f065022cab | ||
|
|
9b20802b4c | ||
|
|
42671400bb | ||
|
|
99ffcdd199 | ||
|
|
90bc303c7c | ||
|
|
04967763e7 | ||
|
|
b47fd08f71 | ||
|
|
49b659a9ae | ||
|
|
fdd0e2e457 | ||
|
|
11c275fa53 | ||
|
|
aa5ce6200d | ||
|
|
9265ba743e | ||
|
|
ccc34b049a | ||
|
|
19c81aa76f | ||
|
|
9f52fc4e3b | ||
|
|
5e5c8007e2 | ||
|
|
8770638d71 | ||
|
|
899ffd4086 | ||
|
|
b06cffac45 | ||
|
|
2968e74714 | ||
|
|
ab4ac2e568 | ||
|
|
919b5ac06d | ||
|
|
9a140f90d4 | ||
|
|
0a396b4dfa | ||
|
|
3800ccee1d | ||
|
|
d29740f0d7 | ||
|
|
41beb8af9c | ||
|
|
579aa5bd1a | ||
|
|
d2b8d3ae7a | ||
|
|
e672a0ebe2 | ||
|
|
f12a578efe | ||
|
|
0ac5e2aa76 | ||
|
|
7593537f3d | ||
|
|
fc95877177 | ||
|
|
6475f29d45 | ||
|
|
7a1e3ce4d9 | ||
|
|
90acd79345 | ||
|
|
9ed43ebbc2 | ||
|
|
96274e9c77 | ||
|
|
0bb34e0d01 | ||
|
|
85b43d1f5c | ||
|
|
4726c5cec3 | ||
|
|
19918843a4 | ||
|
|
0ed7827fe4 | ||
|
|
ff1d4219e3 | ||
|
|
b884059ecd | ||
|
|
16cd971682 | ||
|
|
4f87d72bcd | ||
|
|
ebb8d4a23b | ||
|
|
1a97848d9c | ||
|
|
5620f360c8 | ||
|
|
854f243fb1 | ||
|
|
a98ff71188 | ||
|
|
965cf9aac6 | ||
|
|
613c3b41f8 | ||
|
|
6619870847 | ||
|
|
1ff717ecef | ||
|
|
203021ecb7 | ||
|
|
d67e9eec34 | ||
|
|
a0c57d20d8 | ||
|
|
b217221cef | ||
|
|
6fb318c788 | ||
|
|
c167201d7c | ||
|
|
eea5d610ba | ||
|
|
3b7ce7691e | ||
|
|
2947d22392 | ||
|
|
8de8ae03f3 | ||
|
|
916f58cd9d | ||
|
|
35910a6dbe | ||
|
|
7d14a8b742 | ||
|
|
05862e8bc0 | ||
|
|
cfdf5ff5cb | ||
|
|
f7a28bcf71 | ||
|
|
87e0f2539c | ||
|
|
f4e2736475 | ||
|
|
b9596b780b | ||
|
|
5ad0214a1e | ||
|
|
349c4f706f | ||
|
|
850c17dde1 | ||
|
|
0bdf75a4a0 | ||
|
|
519a444617 | ||
|
|
70be40468a | ||
|
|
5c4a92a6d9 | ||
|
|
6768406422 | ||
|
|
f39d654725 | ||
|
|
1bbce77c92 | ||
|
|
b245aab9d8 | ||
|
|
c15d362865 | ||
|
|
bbdd886a6c | ||
|
|
023e222029 | ||
|
|
8d66536e7f | ||
|
|
d1af8cfbe0 | ||
|
|
19119b573d | ||
|
|
927dbc7e11 | ||
|
|
fe0ea40dc0 | ||
|
|
29541617de | ||
|
|
acef279091 | ||
|
|
b1e766d61a | ||
|
|
6d22474af9 | ||
|
|
b86f1c0aa4 | ||
|
|
55d4cd2aa5 | ||
|
|
865d84f4e7 | ||
|
|
d9da0dd671 | ||
|
|
f54c468f51 | ||
|
|
1c7e6d7d81 | ||
|
|
03c48a5ef3 | ||
|
|
b3aaca317c | ||
|
|
c0efc232ad | ||
|
|
9b94a4a1ed | ||
|
|
6a717f2272 | ||
|
|
475e2a64ce | ||
|
|
865bfdbd34 | ||
|
|
b4ab1c2c69 | ||
|
|
ed1a5b600d | ||
|
|
069ada8c4a | ||
|
|
66b9467e6d | ||
|
|
a60000d631 | ||
|
|
1217aee296 | ||
|
|
664c3ad466 | ||
|
|
ea13375109 | ||
|
|
a3682b72e1 | ||
|
|
92668fda0d | ||
|
|
a4f675c632 | ||
|
|
77b593dae5 | ||
|
|
b05671b8b3 | ||
|
|
5bcb22cd4f | ||
|
|
efb8af902e | ||
|
|
6f62f915d9 | ||
|
|
72c647fba8 | ||
|
|
034814c924 | ||
|
|
78d321b33b | ||
|
|
97cd5e4934 | ||
|
|
3ecd8ce189 | ||
|
|
aa4bc1c776 | ||
|
|
4ea291705a | ||
|
|
4ef09b680f | ||
|
|
ca0e44620a | ||
|
|
c7ba512db5 | ||
|
|
bc46ce30ab | ||
|
|
6c26e2eb51 | ||
|
|
74ef1a4257 | ||
|
|
2412946939 | ||
|
|
b84fe6891c | ||
|
|
4c4b95a39f | ||
|
|
b31a1e2584 | ||
|
|
33a3674989 | ||
|
|
e457ed194f | ||
|
|
ef6bc042fd | ||
|
|
2ba6916e53 | ||
|
|
2d1299bb46 | ||
|
|
ec71fb22de | ||
|
|
f1bff1b62c | ||
|
|
d39060e0a8 | ||
|
|
610edc9584 | ||
|
|
e58033aff0 | ||
|
|
2c80823d96 | ||
|
|
ccd0e863db | ||
|
|
82be39af3d | ||
|
|
7d248f29f0 | ||
|
|
a2b8f74ae1 | ||
|
|
8e9dd17dfd | ||
|
|
7dccb21ffc | ||
|
|
8795d72146 | ||
|
|
c14fc2a721 | ||
|
|
e75645334a | ||
|
|
300c0f0ee2 | ||
|
|
c1ded34c12 | ||
|
|
91cc3a3ff5 | ||
|
|
b23f7b0725 | ||
|
|
25b661b908 | ||
|
|
d5025e7db3 | ||
|
|
0e33fb2b64 | ||
|
|
abde5393df | ||
|
|
dc47007566 | ||
|
|
38cabc8afc | ||
|
|
321f621132 | ||
|
|
5ac5baca38 | ||
|
|
d0a55e266c | ||
|
|
ea5d0fbaf2 | ||
|
|
269b9b27c2 | ||
|
|
757cf24d4d | ||
|
|
ba629e94ef | ||
|
|
0bd9426aa7 | ||
|
|
1b80ec289b | ||
|
|
cf30941514 | ||
|
|
6b0892a495 | ||
|
|
b5a241db8d | ||
|
|
07582ecc5f | ||
|
|
6a7ee3df8a | ||
|
|
72d5e94c97 | ||
|
|
1c9457b1ed | ||
|
|
85ccfd883e | ||
|
|
187ccc64de | ||
|
|
8ac7d0a160 | ||
|
|
3682da417a | ||
|
|
c411aa2342 | ||
|
|
d1c3e47392 | ||
|
|
f7d9efc18e | ||
|
|
0093ad7799 | ||
|
|
7dfc654121 | ||
|
|
0664c39533 | ||
|
|
6083d8b7a8 | ||
|
|
26c714da48 | ||
|
|
d000c160b6 | ||
|
|
ed0995fa49 | ||
|
|
5764ea9ade | ||
|
|
07bfe3823c | ||
|
|
97703b0332 | ||
|
|
3ebbc5976f | ||
|
|
15ffce36de | ||
|
|
68f5947b40 | ||
|
|
d503a436d0 | ||
|
|
75ec86eed4 | ||
|
|
73280fd173 | ||
|
|
6149f46621 | ||
|
|
bdb7350994 | ||
|
|
c08d34ead9 | ||
|
|
ddb0ce3175 | ||
|
|
f68f21a264 | ||
|
|
bc62405810 | ||
|
|
b5061da8c3 | ||
|
|
9c591bd42d | ||
|
|
bbcab38590 | ||
|
|
a7b7059916 | ||
|
|
d4b0ca7804 | ||
|
|
552c729c5a | ||
|
|
0994b0f2de | ||
|
|
ecfc5d68c2 | ||
|
|
06a5e76c50 | ||
|
|
87e0d167e2 | ||
|
|
1a5ae40e15 | ||
|
|
6a4581671f | ||
|
|
8bf1d4924b | ||
|
|
90fd0ba392 | ||
|
|
661329b977 | ||
|
|
525383c725 | ||
|
|
0b7c795e2b | ||
|
|
5cfe4db839 | ||
|
|
c53de9fb6b | ||
|
|
90ef64c63e | ||
|
|
55fe725f4b | ||
|
|
9fc0342cf1 | ||
|
|
beeb66e66d | ||
|
|
46ab7b1a36 | ||
|
|
69a43aa667 | ||
|
|
7381ababb8 | ||
|
|
d67df5feb1 | ||
|
|
6c8b228b6c | ||
|
|
86b46353ad | ||
|
|
99994f6278 | ||
|
|
09e9483d94 | ||
|
|
351b9318d4 | ||
|
|
6a67a1d48a | ||
|
|
19f8b13cfe | ||
|
|
ab6744df3a | ||
|
|
5d54f567cd | ||
|
|
c413a6cab5 | ||
|
|
3b70ae4d9f | ||
|
|
b97cf78d9d | ||
|
|
aff328c660 | ||
|
|
74dc9957f2 | ||
|
|
479f20dabf | ||
|
|
6d6f22788d | ||
|
|
2e4c6eae96 | ||
|
|
07a830e9a6 | ||
|
|
13eb3bccd0 | ||
|
|
87ca2dfb32 | ||
|
|
49a50b32a7 | ||
|
|
a5ba262c4c | ||
|
|
fd2410d2ae | ||
|
|
766b2ba13e | ||
|
|
6cade89369 | ||
|
|
9e234b7600 | ||
|
|
08b3182ef3 | ||
|
|
211bcf8948 | ||
|
|
bd57955168 | ||
|
|
a1db69b7e7 | ||
|
|
378bb5fda3 | ||
|
|
be7a6e55f8 | ||
|
|
5ce3de4933 | ||
|
|
b570368f10 | ||
|
|
c73ef2c649 | ||
|
|
6ce8781af1 | ||
|
|
713928cff3 | ||
|
|
2a0a69f3ed | ||
|
|
4de95913a5 | ||
|
|
513bf7eaca | ||
|
|
d5f180db4f | ||
|
|
db1674811f | ||
|
|
005a239932 | ||
|
|
efeb898be1 | ||
|
|
5a7c43ab32 | ||
|
|
8a8638a817 | ||
|
|
ee065bc64c | ||
|
|
27e995eb53 | ||
|
|
1b65c5fbc9 | ||
|
|
4fa9ccd9e4 | ||
|
|
f83d6e61ae | ||
|
|
882d8e5a88 | ||
|
|
a9731cd5bc | ||
|
|
e807a5d20a | ||
|
|
d8996b47b5 | ||
|
|
4fd0e9e02d | ||
|
|
56e261023c | ||
|
|
84d24504d8 | ||
|
|
1d4fbfdf37 | ||
|
|
9ce24ff32c |
@@ -0,0 +1,47 @@
|
||||
name: Nightly Security Scan
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # 12am UTC daily runtime
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
scan-image:
|
||||
name: Scan Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build the Docker image
|
||||
run: docker build . -t appwrite_image:latest
|
||||
- name: Run Trivy vulnerability scanner on image
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
image-ref: 'appwrite_image:latest'
|
||||
format: 'sarif'
|
||||
output: 'trivy-image-results.sarif'
|
||||
ignore-unfixed: 'false'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Upload Docker Image Scan Results
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: 'trivy-image-results.sarif'
|
||||
|
||||
scan-code:
|
||||
name: Scan Code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Trivy vulnerability scanner on filesystem
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
format: 'sarif'
|
||||
output: 'trivy-fs-results.sarif'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Upload Code Scan Results
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: 'trivy-fs-results.sarif'
|
||||
@@ -1,17 +1,22 @@
|
||||
name: PR Security Scan
|
||||
on:
|
||||
pull_request:
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Build the Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
@@ -19,6 +24,7 @@ jobs:
|
||||
push: false
|
||||
load: true
|
||||
tags: pr_image:${{ github.sha }}
|
||||
|
||||
- name: Run Trivy vulnerability scanner on image
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
@@ -26,6 +32,7 @@ jobs:
|
||||
format: 'json'
|
||||
output: 'trivy-image-results.json'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Run Trivy vulnerability scanner on source code
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
@@ -34,10 +41,11 @@ jobs:
|
||||
format: 'json'
|
||||
output: 'trivy-fs-results.json'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Process and post Trivy scan results
|
||||
|
||||
- name: Process Trivy scan results
|
||||
id: process-results
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
let commentBody = '## Security Scan Results for PR\n\n';
|
||||
@@ -79,9 +87,19 @@ jobs:
|
||||
commentBody += 'Please contact the core team for assistance.';
|
||||
}
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: commentBody
|
||||
});
|
||||
core.setOutput('comment-body', commentBody);
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Security Scan Results for PR
|
||||
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
body: ${{ steps.process-results.outputs.comment-body }}
|
||||
edit-mode: replace
|
||||
|
||||
+15
-14
@@ -16,15 +16,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build Appwrite
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
VERSION=dev
|
||||
|
||||
- name: Cache Docker Image
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
@@ -51,10 +51,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
@@ -81,10 +81,10 @@ jobs:
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
@@ -113,6 +113,7 @@ jobs:
|
||||
Console,
|
||||
Databases,
|
||||
Functions,
|
||||
FunctionsSchedule,
|
||||
GraphQL,
|
||||
Health,
|
||||
Locale,
|
||||
@@ -128,10 +129,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
@@ -141,7 +142,7 @@ jobs:
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
docker compose up -d
|
||||
sleep 25
|
||||
sleep 30
|
||||
|
||||
- name: Run ${{matrix.service}} Tests
|
||||
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||
@@ -149,15 +150,15 @@ jobs:
|
||||
- name: Run ${{matrix.service}} Shared Tables Tests
|
||||
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
|
||||
|
||||
benchamrking:
|
||||
benchmarking:
|
||||
name: Benchmark
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
|
||||
+142
@@ -1,3 +1,145 @@
|
||||
# Version 1.6.0
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Allow execution filter attributes in [#7607](https://github.com/appwrite/appwrite/pull/7607)
|
||||
* Add dynamic API keys for function executions in [#7512](https://github.com/appwrite/appwrite/pull/7512)
|
||||
* Add metrics for successful and failed builds in [#8210](https://github.com/appwrite/appwrite/pull/8210)
|
||||
* Update logging config to use a DSN approach in [#8187](https://github.com/appwrite/appwrite/pull/8187)
|
||||
* Add projects.createJWT endpoint for dynamic keys in [#8213](https://github.com/appwrite/appwrite/pull/8213)
|
||||
* Add users.createJWT() endpoint for local function development in [#8207](https://github.com/appwrite/appwrite/pull/8207)
|
||||
* Added cancel build endpoint in [#7605](https://github.com/appwrite/appwrite/pull/7605)
|
||||
* Add CLI as a function deployment type in [#8215](https://github.com/appwrite/appwrite/pull/8215)
|
||||
* Add vcs.getRepositoryContents() endpoint in [#8330](https://github.com/appwrite/appwrite/pull/8330)
|
||||
* Add appwrite version in function variables in [#8336](https://github.com/appwrite/appwrite/pull/8336)
|
||||
* Add support for scheduled executions in [#8243](https://github.com/appwrite/appwrite/pull/8243)
|
||||
* Add endpoint to delete execution in [#8337](https://github.com/appwrite/appwrite/pull/8337)
|
||||
* OPR v4 support in [#8323](https://github.com/appwrite/appwrite/pull/8323)
|
||||
* Mock OTP and phone numbers in [#7565](https://github.com/appwrite/appwrite/pull/7565)
|
||||
* Support scheduled executions in [#8355](https://github.com/appwrite/appwrite/pull/8355)
|
||||
* Add alert for new sessions in [#8315](https://github.com/appwrite/appwrite/pull/8315)
|
||||
* Update delete authenticator to remove OTP Validation in [#8367](https://github.com/appwrite/appwrite/pull/8367)
|
||||
* Track project last activity in [#8366](https://github.com/appwrite/appwrite/pull/8366)
|
||||
* Containerize the console in [#8406](https://github.com/appwrite/appwrite/pull/8406)
|
||||
* Implement MBSeconds Metric on 1.5.X in [#8385](https://github.com/appwrite/appwrite/pull/8385)
|
||||
* Support JWTs without session ID in [#8420](https://github.com/appwrite/appwrite/pull/8420)
|
||||
* 1.6.x sdks in [#8359](https://github.com/appwrite/appwrite/pull/8359)
|
||||
* Base migration for 1.6.x in [#8417](https://github.com/appwrite/appwrite/pull/8417)
|
||||
* 1.6.x migrations and filters in [#8403](https://github.com/appwrite/appwrite/pull/8403)
|
||||
* Add APPWRITE_REGION in function variables in [#8394](https://github.com/appwrite/appwrite/pull/8394)
|
||||
* Support dynamic keys for domain executions in [#8428](https://github.com/appwrite/appwrite/pull/8428)
|
||||
* Bump DBIP to latest version in [#8467](https://github.com/appwrite/appwrite/pull/8467)
|
||||
* Automatically restart function on crash in [#8473](https://github.com/appwrite/appwrite/pull/8473)
|
||||
* Don't send session alerts for otp and magic-url logins in [#8459](https://github.com/appwrite/appwrite/pull/8459)
|
||||
* Mark 4XX executions as successful in [#8493](https://github.com/appwrite/appwrite/pull/8493)
|
||||
* Add dynamic keys in builds in [#8492](https://github.com/appwrite/appwrite/pull/8492)
|
||||
* Allow deployment queries on type and size in [#8515](https://github.com/appwrite/appwrite/pull/8515)
|
||||
* Add OTP email template in [#8501](https://github.com/appwrite/appwrite/pull/8501)
|
||||
* Update console links in [#8523](https://github.com/appwrite/appwrite/pull/8523)
|
||||
* Add multipart support in [#8477](https://github.com/appwrite/appwrite/pull/8477)
|
||||
* Separate deployment sizes in [#8556](https://github.com/appwrite/appwrite/pull/8556)
|
||||
* Add go runtime in [#8572](https://github.com/appwrite/appwrite/pull/8572)
|
||||
* Add react native platform in [#8562](https://github.com/appwrite/appwrite/pull/8562)
|
||||
* Merge deployments and build storage metrics together in API in [#8443](https://github.com/appwrite/appwrite/pull/8443)
|
||||
* Support string attribute resizing in [#8597](https://github.com/appwrite/appwrite/pull/8597)
|
||||
* Support renaming attributes in [#8544](https://github.com/appwrite/appwrite/pull/8544)
|
||||
* Add VCS vars to deployments & executions in [#8631](https://github.com/appwrite/appwrite/pull/8631)
|
||||
* Function storage metrics in [#8668](https://github.com/appwrite/appwrite/pull/8668)
|
||||
* External messaging usage count in [#8672](https://github.com/appwrite/appwrite/pull/8672)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix execution duration in [#8357](https://github.com/appwrite/appwrite/pull/8357)
|
||||
* Fix file size calculations in [#8432](https://github.com/appwrite/appwrite/pull/8432)
|
||||
* Fix disabled function logging in [#8398](https://github.com/appwrite/appwrite/pull/8398)
|
||||
* Fix function redeployments in [#8434](https://github.com/appwrite/appwrite/pull/8434)
|
||||
* Add value to variables template in [#8483](https://github.com/appwrite/appwrite/pull/8483)
|
||||
* Fix build size limits in [#8396](https://github.com/appwrite/appwrite/pull/8396)
|
||||
* Fix deployment method name in [#8490](https://github.com/appwrite/appwrite/pull/8490)
|
||||
* Fix function disconnecting from git in [#8500](https://github.com/appwrite/appwrite/pull/8500)
|
||||
* Increase buckets metadata in [#8452](https://github.com/appwrite/appwrite/pull/8452)
|
||||
* Fix deploy from git with space in [#8517](https://github.com/appwrite/appwrite/pull/8517)
|
||||
* Fix missing build logs in [#8484](https://github.com/appwrite/appwrite/pull/8484)
|
||||
* Delete team memberships synchronously in [#8217](https://github.com/appwrite/appwrite/pull/8217)
|
||||
* Fix Anyof validator in specs in [#8543](https://github.com/appwrite/appwrite/pull/8543)
|
||||
* Fix missing function variables in [#8554](https://github.com/appwrite/appwrite/pull/8554)
|
||||
* Fix deadlock in [#8609](https://github.com/appwrite/appwrite/pull/8609)
|
||||
* Fix domain execution stats in [#8608](https://github.com/appwrite/appwrite/pull/8608)
|
||||
* Update console redirect to include query params in [#8619](https://github.com/appwrite/appwrite/pull/8619)
|
||||
* Update abuse-key for mfa challenge endpoints in [#8649](https://github.com/appwrite/appwrite/pull/8649)
|
||||
* Fix cross-project scheduler stability in [#8641](https://github.com/appwrite/appwrite/pull/8641)
|
||||
* Fix vcs deployment size in [#8640](https://github.com/appwrite/appwrite/pull/8640)
|
||||
* Fix logging behaviour for Functions in [#8627](https://github.com/appwrite/appwrite/pull/8627)
|
||||
* Add retention env vars to deletes worker in [#8662](https://github.com/appwrite/appwrite/pull/8662)
|
||||
* Fix scheduled executions data in [#8639](https://github.com/appwrite/appwrite/pull/8639)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Sync 1.6.x with main in [#8163](https://github.com/appwrite/appwrite/pull/8163)
|
||||
* Remove build ID from rebuild deployment endpoint in [#8214](https://github.com/appwrite/appwrite/pull/8214)
|
||||
* 1.6.x specs in [#8304](https://github.com/appwrite/appwrite/pull/8304)
|
||||
* Sync with main in [#8295](https://github.com/appwrite/appwrite/pull/8295)
|
||||
* Fix 1.6.x failing tests in [#8333](https://github.com/appwrite/appwrite/pull/8333)
|
||||
* Ensure CI/CD works in [#8350](https://github.com/appwrite/appwrite/pull/8350)
|
||||
* Update specs in [#8356](https://github.com/appwrite/appwrite/pull/8356)
|
||||
* Sync main to 1.6.x in [#8430](https://github.com/appwrite/appwrite/pull/8430)
|
||||
* Add scheduledAt in execution response model in [#8425](https://github.com/appwrite/appwrite/pull/8425)
|
||||
* Move functions marketplace to appwrite in [#8427](https://github.com/appwrite/appwrite/pull/8427)
|
||||
* Refactor deployment check in function tests in [#8444](https://github.com/appwrite/appwrite/pull/8444)
|
||||
* Add ci/cd benchmark in [#8414](https://github.com/appwrite/appwrite/pull/8414)
|
||||
* Upgrade SDK version in [#8465](https://github.com/appwrite/appwrite/pull/8465)
|
||||
* Improve session alert in [#8399](https://github.com/appwrite/appwrite/pull/8399)
|
||||
* Address review comments in [#8422](https://github.com/appwrite/appwrite/pull/8422)
|
||||
* Add scopes to function template in [#8496](https://github.com/appwrite/appwrite/pull/8496)
|
||||
* Update benchmark comment in [#8507](https://github.com/appwrite/appwrite/pull/8507)
|
||||
* Add key to runtime model in [#8503](https://github.com/appwrite/appwrite/pull/8503)
|
||||
* Upgrade logger in [#8497](https://github.com/appwrite/appwrite/pull/8497)
|
||||
* Change default email addresses in [#8466](https://github.com/appwrite/appwrite/pull/8466)
|
||||
* Improve scheduled executions in [#8412](https://github.com/appwrite/appwrite/pull/8412)
|
||||
* Sync 1.5.x into main in [#8509](https://github.com/appwrite/appwrite/pull/8509)
|
||||
* Sync 1.6 with main in [#8529](https://github.com/appwrite/appwrite/pull/8529)
|
||||
* Fix templates CORS in [#8528](https://github.com/appwrite/appwrite/pull/8528)
|
||||
* Update size to specification for variable runtimes in [#8537](https://github.com/appwrite/appwrite/pull/8537)
|
||||
* Add boundary to multipart header in [#8539](https://github.com/appwrite/appwrite/pull/8539)
|
||||
* Support manual templates in [#8527](https://github.com/appwrite/appwrite/pull/8527)
|
||||
* Reorder runtimes in [#8540](https://github.com/appwrite/appwrite/pull/8540)
|
||||
* Fix 1.6 bugs in [#8358](https://github.com/appwrite/appwrite/pull/8358)
|
||||
* Add seconds precision to scheduledAt in [#8546](https://github.com/appwrite/appwrite/pull/8546)
|
||||
* Update docker base image in [#8485](https://github.com/appwrite/appwrite/pull/8485)
|
||||
* Update create execution return type in [#8542](https://github.com/appwrite/appwrite/pull/8542)
|
||||
* Default fallback to for templateBranch in [#8547](https://github.com/appwrite/appwrite/pull/8547)
|
||||
* Fix env vars functions test in [#8555](https://github.com/appwrite/appwrite/pull/8555)
|
||||
* Fix session alerts in [#8550](https://github.com/appwrite/appwrite/pull/8550)
|
||||
* Add runtime controls in [#8384](https://github.com/appwrite/appwrite/pull/8384)
|
||||
* Revert request type to json in create execution in [#8563](https://github.com/appwrite/appwrite/pull/8563)
|
||||
* Sync 1.6.x Filters and Migrations with latest in [#8553](https://github.com/appwrite/appwrite/pull/8553)
|
||||
* Update sdks in [#8551](https://github.com/appwrite/appwrite/pull/8551)
|
||||
* Update Docs in [#8567](https://github.com/appwrite/appwrite/pull/8567)
|
||||
* Headers validator benchmark in [#8561](https://github.com/appwrite/appwrite/pull/8561)
|
||||
* Fix go version in [#8571](https://github.com/appwrite/appwrite/pull/8571)
|
||||
* Update dependencies in [#8574](https://github.com/appwrite/appwrite/pull/8574)
|
||||
* Upgrade console in [#8575](https://github.com/appwrite/appwrite/pull/8575)
|
||||
* 1.6.x logging test in [#8580](https://github.com/appwrite/appwrite/pull/8580)
|
||||
* Bump console sdk in [#8581](https://github.com/appwrite/appwrite/pull/8581)
|
||||
* Update sdks in [#8582](https://github.com/appwrite/appwrite/pull/8582)
|
||||
* Add changelogs for dart and flutter in [#8587](https://github.com/appwrite/appwrite/pull/8587)
|
||||
* Add payload validator in [#8594](https://github.com/appwrite/appwrite/pull/8594)
|
||||
* Update geodb in [#8615](https://github.com/appwrite/appwrite/pull/8615)
|
||||
* Update createdeployment methodtype to upload in [#8616](https://github.com/appwrite/appwrite/pull/8616)
|
||||
* Remove tenant in document filter in [#8624](https://github.com/appwrite/appwrite/pull/8624)
|
||||
* Improve mail datetime format in [#8628](https://github.com/appwrite/appwrite/pull/8628)
|
||||
* Fix router function execution logging in [#8625](https://github.com/appwrite/appwrite/pull/8625)
|
||||
* Add Functions templates async test in [#8622](https://github.com/appwrite/appwrite/pull/8622)
|
||||
* Update console in [#8629](https://github.com/appwrite/appwrite/pull/8629)
|
||||
* 1.6.1 in [#8630](https://github.com/appwrite/appwrite/pull/8630)
|
||||
* Update version in [#8646](https://github.com/appwrite/appwrite/pull/8646)
|
||||
* Phone auth metric rename in [#8648](https://github.com/appwrite/appwrite/pull/8648)
|
||||
* Pretty print specs in [#8643](https://github.com/appwrite/appwrite/pull/8643)
|
||||
* Fix messaging metrics in [#8674](https://github.com/appwrite/appwrite/pull/8674)
|
||||
* Bump console to 5.0.6 in [#8585](https://github.com/appwrite/appwrite/pull/8585)
|
||||
|
||||
# Version 1.5.10
|
||||
|
||||
## What's Changed
|
||||
|
||||
@@ -319,10 +319,13 @@ These are the current metrics we collect usage stats for:
|
||||
| users | Total number of users per project|
|
||||
| executions | Total number of executions per project |
|
||||
| databases | Total number of databases per project |
|
||||
| databases.storage | Total amount of storage used by all databases per project (in bytes) |
|
||||
| collections | Total number of collections per project |
|
||||
| {databaseInternalId}.collections | Total number of collections per database|
|
||||
| {databaseInternalId}.storage | Sum of database storage (in bytes) |
|
||||
| documents | Total number of documents per project |
|
||||
| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
|
||||
| {databaseInternalId}.{collectionInternalId}.storage | Sum of database storage used by the collection (in bytes) |
|
||||
| buckets | Total number of buckets per project |
|
||||
| files | Total number of files per project |
|
||||
| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
FROM composer:2.0 as composer
|
||||
FROM composer:2.0 AS composer
|
||||
|
||||
ARG TESTING=false
|
||||
ENV TESTING=$TESTING
|
||||
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
|
||||
--no-plugins --no-scripts --prefer-dist \
|
||||
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
|
||||
|
||||
FROM appwrite/base:0.9.2 as final
|
||||
FROM appwrite/base:0.9.3 AS final
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
@@ -28,6 +28,8 @@ RUN \
|
||||
apk add boost boost-dev; \
|
||||
fi
|
||||
|
||||
RUN apk add libwebp
|
||||
|
||||
WORKDIR /usr/src/code
|
||||
|
||||
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
|
||||
|
||||
+3
-3
@@ -67,7 +67,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -79,7 +79,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -89,7 +89,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
> Our Appwrite Init event has concluded. You can check out all the new and upcoming features [on our Init website](https://appwrite.io/init) 🚀
|
||||
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) 🚀
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
@@ -75,7 +75,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -87,7 +87,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -97,7 +97,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.5.10
|
||||
appwrite/appwrite:1.6.0
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
@@ -134,6 +134,12 @@ Choose from one of the providers below:
|
||||
<br /><sub><b>Akamai Compute</b></sub></a>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100" height="100">
|
||||
<a href="https://aws.amazon.com/marketplace/pp/prodview-2hiaeo2px4md6">
|
||||
<img width="50" height="39" src="public/images/integrations/aws-logo.svg" alt="AWS Logo" />
|
||||
<br /><sub><b>AWS Marketplace</b></sub></a>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
| 1.3.x | :white_check_mark: |
|
||||
| 1.4.x | :white_check_mark: |
|
||||
| 1.5.x | :white_check_mark: |
|
||||
| 1.6.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+13
-3
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\CLI;
|
||||
@@ -23,6 +23,12 @@ use Utopia\Queue\Connection;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
|
||||
// overwriting runtimes to be architectur agnostic for CLI
|
||||
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
|
||||
|
||||
// require controllers after overwriting runtimes
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
CLI::setResource('register', fn () => $register);
|
||||
@@ -191,8 +197,12 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Usage stats log pushed with status code: ' . $responseCode);
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
|
||||
@@ -46,15 +46,15 @@ $commonCollections = [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('mimeType'),
|
||||
'$id' => ID::custom('headers'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => [],
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => 'accessedAt',
|
||||
@@ -2406,6 +2406,17 @@ $projectCollections = array_merge([
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('originalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
@@ -4109,13 +4120,24 @@ $projectCollections = array_merge([
|
||||
'$id' => ID::custom('source'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 8192,
|
||||
'size' => 8192, // reduce size
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('destination'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false, // make true after patch script
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('credentials'),
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -4508,6 +4530,28 @@ $consoleCollections = array_merge([
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('pingCount'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('pingedAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
@@ -4531,6 +4575,20 @@ $consoleCollections = array_merge([
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_pingCount'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['pingCount'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_pingedAt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['pingedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
@@ -4688,7 +4746,7 @@ $consoleCollections = array_merge([
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
|
||||
@@ -24,6 +24,11 @@ return [
|
||||
'description' => 'Access to this API is forbidden.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_RESOURCE_BLOCKED => [
|
||||
'name' => Exception::GENERAL_RESOURCE_BLOCKED,
|
||||
'description' => 'Access to this resource is blocked.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_UNKNOWN_ORIGIN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
|
||||
'description' => 'The request originated from an unknown origin. If you trust this domain, please list it as a trusted platform in the Appwrite console.',
|
||||
@@ -699,6 +704,11 @@ return [
|
||||
'description' => 'The relationship value is invalid.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_INVALID_RESIZE => [
|
||||
'name' => Exception::ATTRIBUTE_INVALID_RESIZE,
|
||||
'description' => "Existing data is too large for new size, truncate your existing data then try again.",
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Indexes */
|
||||
Exception::INDEX_NOT_FOUND => [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
const TEMPLATE_RUNTIMES = [
|
||||
'NODE' => [
|
||||
'name' => 'node',
|
||||
'versions' => ['21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
],
|
||||
'PYTHON' => [
|
||||
'name' => 'python',
|
||||
@@ -11,7 +11,7 @@ const TEMPLATE_RUNTIMES = [
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
|
||||
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
@@ -21,9 +21,13 @@ const TEMPLATE_RUNTIMES = [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'DENO' => [
|
||||
'name' => 'deno',
|
||||
'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21']
|
||||
],
|
||||
'BUN' => [
|
||||
'name' => 'bun',
|
||||
'versions' => ['1.0']
|
||||
'versions' => ['1.1', '1.0']
|
||||
],
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
@@ -51,7 +55,7 @@ return [
|
||||
'id' => 'starter',
|
||||
'name' => 'Starter function',
|
||||
'tagline' =>
|
||||
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
|
||||
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
'cron' => '',
|
||||
@@ -73,6 +77,7 @@ return [
|
||||
'src/index.php',
|
||||
'php/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
],
|
||||
@@ -82,7 +87,7 @@ return [
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
'scopes' => ["users.read"]
|
||||
'scopes' => ['users.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-upstash',
|
||||
@@ -121,11 +126,12 @@ return [
|
||||
'description' => 'Authentication token to access your Upstash Vector database. <a class="u-bold" target="_blank" href="https://upstash.com/docs/vector/overall/getstarted">Learn more</a>.',
|
||||
'value' => '',
|
||||
'placeholder' =>
|
||||
'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
|
||||
'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-redis',
|
||||
@@ -167,7 +173,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-neo4j',
|
||||
@@ -217,14 +224,15 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-mongodb',
|
||||
'id' => 'query-mongo-atlas',
|
||||
'name' => 'Query MongoDB Atlas',
|
||||
'tagline' =>
|
||||
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
|
||||
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
'cron' => '',
|
||||
@@ -249,18 +257,19 @@ return [
|
||||
'description' => 'The endpoint to connect to your Mongo database. <a class="u-bold" target="_blank" href="https://www.mongodb.com/docs/atlas/getting-started/">Learn more</a>.',
|
||||
'value' => '',
|
||||
'placeholder' =>
|
||||
'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
|
||||
'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-neon',
|
||||
'id' => 'query-neon-postgres',
|
||||
'name' => 'Query Neon Postgres',
|
||||
'tagline' =>
|
||||
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
|
||||
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
'cron' => '',
|
||||
@@ -320,7 +329,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-open-ai',
|
||||
@@ -380,7 +390,8 @@ return [
|
||||
'required' => false,
|
||||
'type' => 'number'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-discord',
|
||||
@@ -442,7 +453,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-perspective-api',
|
||||
@@ -476,14 +488,15 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-pangea',
|
||||
'id' => 'censor-with-redact',
|
||||
'name' => 'Censor with Redact',
|
||||
'tagline' =>
|
||||
'Censor sensitive information from a provided text string using Redact API by Pangea.',
|
||||
'Censor sensitive information from a provided text string using Redact API by Pangea.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
'cron' => '',
|
||||
@@ -523,7 +536,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-document',
|
||||
@@ -543,14 +557,15 @@ return [
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => []
|
||||
'variables' => [],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-github',
|
||||
'id' => 'github-issue-bot',
|
||||
'name' => 'GitHub issue bot',
|
||||
'tagline' =>
|
||||
'Automate the process of responding to newly opened issues in a GitHub repository.',
|
||||
'Automate the process of responding to newly opened issues in a GitHub repository.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
'cron' => '',
|
||||
@@ -586,7 +601,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-bookmark',
|
||||
@@ -637,7 +653,7 @@ return [
|
||||
'type' => 'url'
|
||||
]
|
||||
],
|
||||
'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
|
||||
'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-algolia',
|
||||
@@ -718,7 +734,7 @@ return [
|
||||
'type' => 'password'
|
||||
],
|
||||
],
|
||||
'scopes' => ["databases.read", "collections.read", "documents.read"]
|
||||
'scopes' => ['databases.read', 'collections.read', 'documents.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-meilisearch',
|
||||
@@ -811,7 +827,7 @@ return [
|
||||
'type' => 'text'
|
||||
],
|
||||
],
|
||||
'scopes' => ["databases.read", "collections.read", "documents.read"]
|
||||
'scopes' => ['databases.read', 'collections.read', 'documents.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-vonage',
|
||||
@@ -896,7 +912,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'phone'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-bell',
|
||||
@@ -951,7 +968,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'url'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-mail',
|
||||
@@ -1033,7 +1051,8 @@ return [
|
||||
'required' => false,
|
||||
'type' => 'text'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-stripe',
|
||||
@@ -1074,7 +1093,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
'scopes' => ["users.read", "sessions.write", "users.write"]
|
||||
'scopes' => ['users.read', 'sessions.write', 'users.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-stripe',
|
||||
@@ -1131,7 +1150,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
|
||||
'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chat',
|
||||
@@ -1164,7 +1183,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-translate',
|
||||
@@ -1197,7 +1217,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-eye',
|
||||
@@ -1255,7 +1276,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
'scopes' => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
|
||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-eye',
|
||||
@@ -1313,7 +1334,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
"scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
|
||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-text',
|
||||
@@ -1371,7 +1392,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
"scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
|
||||
'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chat',
|
||||
@@ -1429,7 +1450,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
|
||||
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1463,7 +1484,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1505,7 +1527,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["buckets.write", "files.read", "files.write"]
|
||||
'scopes' => ['buckets.write', 'files.read', 'files.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1579,7 +1601,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-document-search',
|
||||
@@ -1642,7 +1665,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["databases.read", "collections.read", "documents.read"]
|
||||
'scopes' => ['databases.read', 'collections.read', 'documents.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1705,7 +1728,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["databases.read", "collections.read", "documents.read"]
|
||||
'scopes' => ['databases.read', 'collections.read', 'documents.read']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chat',
|
||||
@@ -1760,7 +1783,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
|
||||
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1801,7 +1824,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
|
||||
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1841,7 +1864,8 @@ return [
|
||||
'required' => false,
|
||||
'type' => 'number'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-music-note',
|
||||
@@ -1883,7 +1907,7 @@ return [
|
||||
'type' => 'password'
|
||||
]
|
||||
],
|
||||
"scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
|
||||
'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-chip',
|
||||
@@ -1917,7 +1941,8 @@ return [
|
||||
'required' => true,
|
||||
'type' => 'password'
|
||||
]
|
||||
]
|
||||
],
|
||||
'scopes' => []
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-currency-dollar',
|
||||
@@ -1972,7 +1997,7 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["users.read", "users.write"]
|
||||
'scopes' => ['users.read', 'users.write']
|
||||
],
|
||||
[
|
||||
'icon' => 'icon-currency-dollar',
|
||||
@@ -2043,6 +2068,6 @@ return [
|
||||
'type' => 'text'
|
||||
]
|
||||
],
|
||||
"scopes" => ["users.read", "users.write"]
|
||||
'scopes' => ['users.read', 'users.write']
|
||||
]
|
||||
];
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"emails.magicSession.signature": "{{project}} team",
|
||||
"emails.sessionAlert.subject": "Security alert: new session on your {{project}} account",
|
||||
"emails.sessionAlert.hello":"Hello {{user}}",
|
||||
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, on {{b}}{{dateTime}}{{/b}}.\nHere are the details of the new session: ",
|
||||
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, {{b}}on {{date}}, {{year}} at {{time}} UTC{{/b}}.\nHere are the details of the new session: ",
|
||||
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
|
||||
"emails.sessionAlert.listIpAddress": "IP Address: {{b}}{{ipAddress}}{{/b}}",
|
||||
"emails.sessionAlert.listCountry": "Country: {{b}}{{country}}{{/b}}",
|
||||
|
||||
+17
-21
@@ -1,9 +1,5 @@
|
||||
<?php
|
||||
|
||||
const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
return [
|
||||
APP_PLATFORM_CLIENT => [
|
||||
'key' => APP_PLATFORM_CLIENT,
|
||||
@@ -15,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '16.0.0-rc.4',
|
||||
'version' => '16.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -63,7 +59,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '13.0.0-rc.3',
|
||||
'version' => '13.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -81,7 +77,7 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '7.0.0-rc.2',
|
||||
'version' => '7.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -116,7 +112,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '6.0.0-rc.2',
|
||||
'version' => '6.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -138,7 +134,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.5.0-rc.2',
|
||||
'version' => '0.5.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -203,7 +199,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Console',
|
||||
'version' => '0.6.5-rc.2',
|
||||
'version' => '1.2.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
'package' => '',
|
||||
'enabled' => true,
|
||||
@@ -221,7 +217,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '6.0.0-rc.8',
|
||||
'version' => '6.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -249,7 +245,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '14.0.0-rc.3',
|
||||
'version' => '14.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -267,7 +263,7 @@ return [
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '12.0.0-rc.2',
|
||||
'version' => '12.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -285,7 +281,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '12.0.0-rc.2',
|
||||
'version' => '12.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -303,7 +299,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '6.0.0-rc.2',
|
||||
'version' => '6.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -321,7 +317,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '12.0.0-rc.3',
|
||||
'version' => '12.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -339,7 +335,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.0.1-rc.8',
|
||||
'version' => '0.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -357,7 +353,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.9.0-rc.2',
|
||||
'version' => '0.10.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -375,7 +371,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '12.0.0-rc.3',
|
||||
'version' => '12.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -393,7 +389,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '6.0.0-rc.2',
|
||||
'version' => '6.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -415,7 +411,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '6.0.0-rc.2',
|
||||
'version' => '6.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
],
|
||||
'blr' => [
|
||||
'$id' => 'blr',
|
||||
'name' => 'Banglore',
|
||||
'name' => 'Bengaluru',
|
||||
'disabled' => true,
|
||||
'flag' => 'in',
|
||||
'default' => true,
|
||||
|
||||
@@ -17,7 +17,6 @@ $member = [
|
||||
'files.read',
|
||||
'files.write',
|
||||
'projects.read',
|
||||
'projects.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'execution.read',
|
||||
@@ -49,6 +48,7 @@ $admins = [
|
||||
'collections.write',
|
||||
'platforms.read',
|
||||
'platforms.write',
|
||||
'projects.write',
|
||||
'keys.read',
|
||||
'keys.write',
|
||||
'webhooks.read',
|
||||
@@ -75,7 +75,7 @@ $admins = [
|
||||
'topics.write',
|
||||
'topics.read',
|
||||
'subscribers.write',
|
||||
'subscribers.read'
|
||||
'subscribers.read',
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
@@ -6,7 +6,7 @@ return [
|
||||
Specification::S_05VCPU_512MB => [
|
||||
'slug' => Specification::S_05VCPU_512MB,
|
||||
'memory' => 512,
|
||||
'cpus' => 0.5
|
||||
'cpus' => 1 // TODO: revert this, it's a temporary change to test function performance.
|
||||
],
|
||||
Specification::S_1VCPU_512MB => [
|
||||
'slug' => Specification::S_1VCPU_512MB,
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,6 +6,8 @@ return [
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
// 'image/heic',
|
||||
'image/avif',
|
||||
|
||||
// Video Files
|
||||
'video/mp4',
|
||||
@@ -31,6 +33,7 @@ return [
|
||||
'audio/ogg', // Ogg Vorbis RFC 5334
|
||||
'audio/vorbis', // Vorbis RFC 5215
|
||||
'audio/vnd.wav', // wav RFC 2361
|
||||
'audio/x-wav', // php reads .wav as this - https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
'audio/aac', //AAC audio
|
||||
'audio/x-hx-aac-adts', // AAC audio
|
||||
|
||||
|
||||
@@ -6,4 +6,7 @@ return [ // Accepted outputs files
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
'webp' => 'image/webp',
|
||||
// 'heic' => 'image/heic',
|
||||
// 'heics' => 'image/heic',
|
||||
'avif' => 'image/avif'
|
||||
];
|
||||
|
||||
@@ -193,7 +193,7 @@ return [
|
||||
'introduction' => '1.5.1',
|
||||
'default' => '',
|
||||
'required' => true,
|
||||
'question' => '',
|
||||
'question' => 'Enter an email that will be used when registering for SSL certificates',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
|
||||
@@ -42,6 +42,7 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -124,7 +125,9 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
'dateTime' => DateTime::format(new \DateTime(), 'h:ia MMMM dS'),
|
||||
'date' => (new \DateTime())->format('F j'),
|
||||
'year' => (new \DateTime())->format('YYYY'),
|
||||
'time' => (new \DateTime())->format('H:i:s'),
|
||||
'user' => $user->getAttribute('name'),
|
||||
'project' => $project->getAttribute('name'),
|
||||
'device' => $session->getAttribute('clientName'),
|
||||
@@ -392,7 +395,7 @@ App::post('/v1/account')
|
||||
$existingTarget = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$email]),
|
||||
]);
|
||||
if($existingTarget) {
|
||||
if ($existingTarget) {
|
||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -3681,7 +3684,7 @@ App::patch('/v1/account/mfa')
|
||||
});
|
||||
|
||||
App::get('/v1/account/mfa/factors')
|
||||
->desc('List Factors')
|
||||
->desc('List factors')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
@@ -3713,7 +3716,7 @@ App::get('/v1/account/mfa/factors')
|
||||
});
|
||||
|
||||
App::post('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Create Authenticator')
|
||||
->desc('Create authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -3785,7 +3788,7 @@ App::post('/v1/account/mfa/authenticators/:type')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Verify Authenticator')
|
||||
->desc('Verify authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -3850,7 +3853,7 @@ App::put('/v1/account/mfa/authenticators/:type')
|
||||
});
|
||||
|
||||
App::post('/v1/account/mfa/recovery-codes')
|
||||
->desc('Create MFA Recovery Codes')
|
||||
->desc('Create MFA recovery codes')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -3892,7 +3895,7 @@ App::post('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::patch('/v1/account/mfa/recovery-codes')
|
||||
->desc('Regenerate MFA Recovery Codes')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -3933,7 +3936,7 @@ App::patch('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::get('/v1/account/mfa/recovery-codes')
|
||||
->desc('Get MFA Recovery Codes')
|
||||
->desc('Get MFA recovery codes')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('scope', 'account')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
@@ -3963,7 +3966,7 @@ App::get('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::delete('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Delete Authenticator')
|
||||
->desc('Delete authenticator')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('event', 'users.[userId].delete.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -4001,7 +4004,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
|
||||
});
|
||||
|
||||
App::post('/v1/account/mfa/challenge')
|
||||
->desc('Create MFA Challenge')
|
||||
->desc('Create MFA challenge')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].challenges.[challengeId].create')
|
||||
@@ -4016,7 +4019,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MFA_CHALLENGE)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},token:{param-token}')
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->param('factor', '', new WhiteList([Type::EMAIL, Type::PHONE, Type::TOTP, Type::RECOVERY_CODE]), 'Factor used for verification. Must be one of following: `' . Type::EMAIL . '`, `' . Type::PHONE . '`, `' . Type::TOTP . '`, `' . Type::RECOVERY_CODE . '`.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -4189,7 +4192,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/challenge')
|
||||
->desc('Create MFA Challenge (confirmation)')
|
||||
->desc('Create MFA challenge (confirmation)')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -4203,7 +4206,7 @@ App::put('/v1/account/mfa/challenge')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'userId:{param-userId}')
|
||||
->label('abuse-key', 'url:{url},challengeId:{param-challengeId}')
|
||||
->param('challengeId', '', new Text(256), 'ID of the challenge.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('project')
|
||||
@@ -4312,7 +4315,7 @@ App::post('/v1/account/targets/push')
|
||||
|
||||
$device = $detector->getDevice();
|
||||
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret);
|
||||
$session = $dbForProject->getDocument('sessions', $sessionId);
|
||||
|
||||
try {
|
||||
@@ -4381,7 +4384,9 @@ App::put('/v1/account/targets/:targetId/push')
|
||||
}
|
||||
|
||||
if ($identifier) {
|
||||
$target->setAttribute('identifier', $identifier);
|
||||
$target
|
||||
->setAttribute('identifier', $identifier)
|
||||
->setAttribute('expired', false);
|
||||
}
|
||||
|
||||
$detector = new Detector($request->getUserAgent());
|
||||
@@ -4450,7 +4455,7 @@ App::delete('/v1/account/targets/:targetId/push')
|
||||
$response->noContent();
|
||||
});
|
||||
App::get('/v1/account/identities')
|
||||
->desc('List Identities')
|
||||
->desc('List identities')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
@@ -4484,6 +4489,12 @@ App::get('/v1/account/identities')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$identityId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('identities', $identityId);
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
|
||||
$output = (empty($output)) ? $type : $output;
|
||||
$data = $image->output($output, $quality);
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/png')
|
||||
->file($data);
|
||||
unset($image);
|
||||
@@ -275,7 +275,7 @@ App::get('/v1/avatars/image')
|
||||
$data = $image->output($output, $quality);
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/png')
|
||||
->file($data);
|
||||
unset($image);
|
||||
@@ -409,7 +409,7 @@ App::get('/v1/avatars/favicon')
|
||||
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
|
||||
}
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/x-icon')
|
||||
->file($data);
|
||||
}
|
||||
@@ -420,7 +420,7 @@ App::get('/v1/avatars/favicon')
|
||||
$data = $image->output($output, $quality);
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/png')
|
||||
->file($data);
|
||||
unset($image);
|
||||
@@ -461,7 +461,7 @@ App::get('/v1/avatars/qr')
|
||||
$image->crop((int) $size, (int) $size);
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->send($image->output('png', 9));
|
||||
});
|
||||
@@ -544,13 +544,13 @@ App::get('/v1/avatars/initials')
|
||||
$image->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->file($image->getImageBlob());
|
||||
});
|
||||
|
||||
App::get('/v1/cards/cloud')
|
||||
->desc('Get Front Of Cloud Card')
|
||||
->desc('Get front Of Cloud Card')
|
||||
->groups(['api', 'avatars'])
|
||||
->label('scope', 'avatars.read')
|
||||
->label('cache', true)
|
||||
@@ -751,13 +751,13 @@ App::get('/v1/cards/cloud')
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->file($baseImage->getImageBlob());
|
||||
});
|
||||
|
||||
App::get('/v1/cards/cloud-back')
|
||||
->desc('Get Back Of Cloud Card')
|
||||
->desc('Get back Of Cloud Card')
|
||||
->groups(['api', 'avatars'])
|
||||
->label('scope', 'avatars.read')
|
||||
->label('cache', true)
|
||||
@@ -829,13 +829,13 @@ App::get('/v1/cards/cloud-back')
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->file($baseImage->getImageBlob());
|
||||
});
|
||||
|
||||
App::get('/v1/cards/cloud-og')
|
||||
->desc('Get OG Image From Cloud Card')
|
||||
->desc('Get OG image From Cloud Card')
|
||||
->groups(['api', 'avatars'])
|
||||
->label('scope', 'avatars.read')
|
||||
->label('cache', true)
|
||||
@@ -1219,7 +1219,7 @@ App::get('/v1/cards/cloud-og')
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->file($baseImage->getImageBlob());
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ App::get('/v1/console/variables')
|
||||
});
|
||||
|
||||
App::post('/v1/console/assistant')
|
||||
->desc('Ask Query')
|
||||
->desc('Ask query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
||||
@@ -5,6 +5,7 @@ use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
@@ -20,12 +21,15 @@ use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception as DatabaseException;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Conflict as ConflictException;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Limit as LimitException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Exception\Restricted as RestrictedException;
|
||||
use Utopia\Database\Exception\Structure as StructureException;
|
||||
use Utopia\Database\Exception\Truncate as TruncateException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -36,6 +40,7 @@ use Utopia\Database\Validator\Index as IndexValidator;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
@@ -228,13 +233,15 @@ function updateAttribute(
|
||||
Database $dbForProject,
|
||||
Event $queueForEvents,
|
||||
string $type,
|
||||
int $size = null,
|
||||
string $filter = null,
|
||||
string|bool|int|float $default = null,
|
||||
bool $required = null,
|
||||
int|float $min = null,
|
||||
int|float $max = null,
|
||||
array $elements = null,
|
||||
array $options = []
|
||||
array $options = [],
|
||||
string $newKey = null,
|
||||
): Document {
|
||||
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
@@ -280,6 +287,10 @@ function updateAttribute(
|
||||
->setAttribute('default', $default)
|
||||
->setAttribute('required', $required);
|
||||
|
||||
if (!empty($size)) {
|
||||
$attribute->setAttribute('size', $size);
|
||||
}
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions');
|
||||
|
||||
switch ($attribute->getAttribute('format')) {
|
||||
@@ -345,6 +356,7 @@ function updateAttribute(
|
||||
$dbForProject->updateRelationship(
|
||||
collection: $collectionId,
|
||||
id: $key,
|
||||
newKey: $newKey,
|
||||
onDelete: $primaryDocumentOptions['onDelete'],
|
||||
);
|
||||
|
||||
@@ -352,22 +364,52 @@ function updateAttribute(
|
||||
$relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']);
|
||||
|
||||
$relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']);
|
||||
|
||||
if (!empty($newKey) && $newKey !== $key) {
|
||||
$options['twoWayKey'] = $newKey;
|
||||
}
|
||||
|
||||
$relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options);
|
||||
$relatedAttribute->setAttribute('options', $relatedOptions);
|
||||
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute);
|
||||
|
||||
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
|
||||
}
|
||||
} else {
|
||||
$dbForProject->updateAttribute(
|
||||
collection: $collectionId,
|
||||
id: $key,
|
||||
required: $required,
|
||||
default: $default,
|
||||
formatOptions: $options ?? null
|
||||
);
|
||||
try {
|
||||
$dbForProject->updateAttribute(
|
||||
collection: $collectionId,
|
||||
id: $key,
|
||||
size: $size,
|
||||
required: $required,
|
||||
default: $default,
|
||||
formatOptions: $options ?? null,
|
||||
newKey: $newKey ?? null
|
||||
);
|
||||
} catch (TruncateException) {
|
||||
throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($newKey) && $key !== $newKey) {
|
||||
// Delete attribute and recreate since we can't modify IDs
|
||||
$original = clone $attribute;
|
||||
|
||||
$dbForProject->deleteDocument('attributes', $attribute->getId());
|
||||
|
||||
$attribute
|
||||
->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey))
|
||||
->setAttribute('key', $newKey);
|
||||
|
||||
try {
|
||||
$attribute = $dbForProject->createDocument('attributes', $attribute);
|
||||
} catch (DatabaseException|PDOException) {
|
||||
$attribute = $dbForProject->createDocument('attributes', $original);
|
||||
}
|
||||
} else {
|
||||
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
|
||||
}
|
||||
|
||||
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
|
||||
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId());
|
||||
|
||||
$queueForEvents
|
||||
@@ -397,6 +439,7 @@ App::post('/v1/databases')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].create')
|
||||
->label('scope', 'databases.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'database.create')
|
||||
->label('audits.resource', 'database/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -412,7 +455,8 @@ App::post('/v1/databases')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->inject('queueForUsage')
|
||||
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) {
|
||||
|
||||
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
|
||||
|
||||
@@ -462,6 +506,7 @@ App::post('/v1/databases')
|
||||
}
|
||||
|
||||
$queueForEvents->setParam('databaseId', $database->getId());
|
||||
$queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -472,6 +517,7 @@ App::get('/v1/databases')
|
||||
->desc('List databases')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'databases.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'list')
|
||||
@@ -503,6 +549,13 @@ App::get('/v1/databases')
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$databaseId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('databases', $databaseId);
|
||||
|
||||
@@ -525,6 +578,7 @@ App::get('/v1/databases/:databaseId')
|
||||
->desc('Get database')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'databases.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'get')
|
||||
@@ -550,6 +604,7 @@ App::get('/v1/databases/:databaseId/logs')
|
||||
->desc('List database logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'databases.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listLogs')
|
||||
@@ -641,6 +696,7 @@ App::put('/v1/databases/:databaseId')
|
||||
->desc('Update database')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'databases.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].update')
|
||||
->label('audits.event', 'database.update')
|
||||
->label('audits.resource', 'database/{response.$id}')
|
||||
@@ -679,6 +735,7 @@ App::delete('/v1/databases/:databaseId')
|
||||
->desc('Delete database')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'databases.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].delete')
|
||||
->label('audits.event', 'database.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}')
|
||||
@@ -693,7 +750,8 @@ App::delete('/v1/databases/:databaseId')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
|
||||
->inject('queueForUsage')
|
||||
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
|
||||
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
|
||||
@@ -716,6 +774,9 @@ App::delete('/v1/databases/:databaseId')
|
||||
->setParam('databaseId', $database->getId())
|
||||
->setPayload($response->output($database, Response::MODEL_DATABASE));
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
@@ -724,6 +785,7 @@ App::post('/v1/databases/:databaseId/collections')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'collection.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -791,6 +853,7 @@ App::get('/v1/databases/:databaseId/collections')
|
||||
->desc('List collections')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listCollections')
|
||||
@@ -831,6 +894,12 @@ App::get('/v1/databases/:databaseId/collections')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$collectionId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
|
||||
@@ -854,6 +923,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->desc('Get collection')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getCollection')
|
||||
@@ -888,6 +958,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
|
||||
->desc('List collection logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listCollectionLogs')
|
||||
@@ -988,6 +1059,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->desc('Update collection')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].update')
|
||||
->label('audits.event', 'collection.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1051,6 +1123,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
|
||||
->desc('Delete collection')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].delete')
|
||||
->label('audits.event', 'collection.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1107,6 +1180,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -1152,6 +1226,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||
'filters' => $filters,
|
||||
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
|
||||
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING);
|
||||
@@ -1163,6 +1238,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1205,6 +1281,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1252,6 +1329,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1294,6 +1372,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1336,6 +1415,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1407,6 +1487,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1481,6 +1562,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1522,6 +1604,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1566,6 +1649,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'attribute.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.namespace', 'databases')
|
||||
@@ -1693,6 +1777,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
|
||||
->desc('List attributes')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listAttributes')
|
||||
@@ -1740,6 +1825,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$attributeId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$collection->getInternalId()]),
|
||||
@@ -1771,6 +1861,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
|
||||
->desc('Get attribute')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getAttribute')
|
||||
@@ -1845,6 +1936,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
|
||||
->desc('Update string attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1859,10 +1951,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('size', null, new Integer(), 'Maximum size of the string attribute.', true)
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
@@ -1871,8 +1965,10 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
|
||||
dbForProject: $dbForProject,
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_STRING,
|
||||
size: $size,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -1884,6 +1980,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
|
||||
->desc('Update email attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1898,10 +1995,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -1911,7 +2009,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
|
||||
type: Database::VAR_STRING,
|
||||
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -1923,6 +2022,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
|
||||
->desc('Update enum attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1938,10 +2038,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
|
||||
->param('elements', null, new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -1952,7 +2053,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
|
||||
filter: APP_DATABASE_ATTRIBUTE_ENUM,
|
||||
default: $default,
|
||||
required: $required,
|
||||
elements: $elements
|
||||
elements: $elements,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -1964,6 +2066,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
|
||||
->desc('Update IP address attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -1978,10 +2081,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new IP()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -1991,7 +2095,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
|
||||
type: Database::VAR_STRING,
|
||||
filter: APP_DATABASE_ATTRIBUTE_IP,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -2003,6 +2108,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
|
||||
->desc('Update URL attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2017,10 +2123,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -2030,7 +2137,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
|
||||
type: Database::VAR_STRING,
|
||||
filter: APP_DATABASE_ATTRIBUTE_URL,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -2042,6 +2150,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
|
||||
->desc('Update integer attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2058,10 +2167,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
|
||||
->param('min', null, new Integer(), 'Minimum value to enforce on new documents')
|
||||
->param('max', null, new Integer(), 'Maximum value to enforce on new documents')
|
||||
->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -2072,7 +2182,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
|
||||
default: $default,
|
||||
required: $required,
|
||||
min: $min,
|
||||
max: $max
|
||||
max: $max,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
@@ -2091,6 +2202,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
|
||||
->desc('Update float attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2107,10 +2219,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
|
||||
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents')
|
||||
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents')
|
||||
->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -2121,7 +2234,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
|
||||
default: $default,
|
||||
required: $required,
|
||||
min: $min,
|
||||
max: $max
|
||||
max: $max,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
@@ -2140,6 +2254,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
|
||||
->desc('Update boolean attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2154,10 +2269,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -2166,7 +2282,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_BOOLEAN,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -2178,6 +2295,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
|
||||
->desc('Update dateTime attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2192,10 +2310,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||
->param('default', null, new Nullable(new DatetimeValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$attribute = updateAttribute(
|
||||
databaseId: $databaseId,
|
||||
collectionId: $collectionId,
|
||||
@@ -2204,7 +2323,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
|
||||
queueForEvents: $queueForEvents,
|
||||
type: Database::VAR_DATETIME,
|
||||
default: $default,
|
||||
required: $required
|
||||
required: $required,
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$response
|
||||
@@ -2216,6 +2336,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
|
||||
->desc('Update relationship attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2229,6 +2350,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
|
||||
->param('key', '', new Key(), 'Attribute Key.')
|
||||
->param('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true)
|
||||
->param('newKey', null, new Key(), 'New attribute key.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
@@ -2237,6 +2359,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
|
||||
string $collectionId,
|
||||
string $key,
|
||||
?string $onDelete,
|
||||
?string $newKey,
|
||||
Response $response,
|
||||
Database $dbForProject,
|
||||
Event $queueForEvents
|
||||
@@ -2251,7 +2374,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
|
||||
required: false,
|
||||
options: [
|
||||
'onDelete' => $onDelete
|
||||
]
|
||||
],
|
||||
newKey: $newKey
|
||||
);
|
||||
|
||||
$options = $attribute->getAttribute('options', []);
|
||||
@@ -2270,6 +2394,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
|
||||
->desc('Delete attribute')
|
||||
->groups(['api', 'database', 'schema'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
|
||||
->label('audits.event', 'attribute.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2286,7 +2411,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
|
||||
->inject('queueForUsage')
|
||||
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
|
||||
|
||||
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
@@ -2371,6 +2497,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
|
||||
->setContext('database', $db)
|
||||
->setPayload($response->output($attribute, $model));
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
@@ -2380,6 +2509,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'index.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -2434,7 +2564,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
'default' => null,
|
||||
'size' => 36
|
||||
'size' => Database::LENGTH_KEY
|
||||
];
|
||||
|
||||
$oldAttributes[] = [
|
||||
@@ -2549,6 +2679,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
->desc('List indexes')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listIndexes')
|
||||
@@ -2592,6 +2723,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$indexId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [
|
||||
Query::equal('collectionInternalId', [$collection->getInternalId()]),
|
||||
@@ -2619,6 +2755,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
|
||||
->desc('Get index')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getIndex')
|
||||
@@ -2658,6 +2795,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
|
||||
->desc('Delete index')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'collections.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update')
|
||||
->label('audits.event', 'index.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
@@ -2723,6 +2861,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'document.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
@@ -2746,8 +2885,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('mode')
|
||||
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) {
|
||||
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) {
|
||||
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
@@ -2963,6 +3103,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->setContext('database', $database)
|
||||
->setPayload($response->getPayload(), sensitive: $relationships);
|
||||
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
|
||||
});
|
||||
|
||||
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
@@ -2970,6 +3113,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->desc('List documents')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listDocuments')
|
||||
@@ -3015,6 +3159,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
|
||||
$documentId = $cursor->getValue();
|
||||
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
|
||||
@@ -3125,6 +3275,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
->desc('Get document')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getDocument')
|
||||
@@ -3217,6 +3368,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
->desc('List document logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'listDocumentLogs')
|
||||
@@ -3322,6 +3474,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update')
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('audits.event', 'document.update')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
@@ -3557,6 +3710,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
->desc('Delete document')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', 'databases')
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete')
|
||||
->label('audits.event', 'document.delete')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}')
|
||||
@@ -3577,10 +3731,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDeletes')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('mode')
|
||||
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) {
|
||||
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage, string $mode) {
|
||||
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -3653,10 +3807,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
)
|
||||
);
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_AUDIT)
|
||||
->setDocument($document);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('databaseId', $databaseId)
|
||||
->setParam('collectionId', $collection->getId())
|
||||
@@ -3665,6 +3815,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
||||
->setContext('database', $database)
|
||||
->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships);
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
@@ -3672,6 +3825,7 @@ App::get('/v1/databases/usage')
|
||||
->desc('Get databases usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getUsage')
|
||||
@@ -3690,6 +3844,7 @@ App::get('/v1/databases/usage')
|
||||
METRIC_DATABASES,
|
||||
METRIC_COLLECTIONS,
|
||||
METRIC_DOCUMENTS,
|
||||
METRIC_DATABASES_STORAGE
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
@@ -3740,9 +3895,11 @@ App::get('/v1/databases/usage')
|
||||
'databasesTotal' => $usage[$metrics[0]]['total'],
|
||||
'collectionsTotal' => $usage[$metrics[1]]['total'],
|
||||
'documentsTotal' => $usage[$metrics[2]]['total'],
|
||||
'storageTotal' => $usage[$metrics[3]]['total'],
|
||||
'databases' => $usage[$metrics[0]]['data'],
|
||||
'collections' => $usage[$metrics[1]]['data'],
|
||||
'documents' => $usage[$metrics[2]]['data'],
|
||||
'storage' => $usage[$metrics[3]]['data'],
|
||||
]), Response::MODEL_USAGE_DATABASES);
|
||||
});
|
||||
|
||||
@@ -3750,6 +3907,7 @@ App::get('/v1/databases/:databaseId/usage')
|
||||
->desc('Get database usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getDatabaseUsage')
|
||||
@@ -3774,6 +3932,7 @@ App::get('/v1/databases/:databaseId/usage')
|
||||
$metrics = [
|
||||
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS),
|
||||
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
|
||||
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE)
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
@@ -3824,8 +3983,10 @@ App::get('/v1/databases/:databaseId/usage')
|
||||
'range' => $range,
|
||||
'collectionsTotal' => $usage[$metrics[0]]['total'],
|
||||
'documentsTotal' => $usage[$metrics[1]]['total'],
|
||||
'storageTotal' => $usage[$metrics[2]]['total'],
|
||||
'collections' => $usage[$metrics[0]]['data'],
|
||||
'documents' => $usage[$metrics[1]]['data'],
|
||||
'storage' => $usage[$metrics[2]]['data'],
|
||||
]), Response::MODEL_USAGE_DATABASE);
|
||||
});
|
||||
|
||||
@@ -3834,6 +3995,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
|
||||
->desc('Get collection usage stats')
|
||||
->groups(['api', 'database', 'usage'])
|
||||
->label('scope', 'collections.read')
|
||||
->label('resourceType', 'databases')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'getCollectionUsage')
|
||||
|
||||
@@ -37,6 +37,7 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Device;
|
||||
@@ -137,6 +138,7 @@ App::post('/v1/functions')
|
||||
->desc('Create function')
|
||||
->label('scope', 'functions.write')
|
||||
->label('event', 'functions.[functionId].create')
|
||||
->label('resourceType', 'functions')
|
||||
->label('audits.event', 'function.create')
|
||||
->label('audits.resource', 'function/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -295,7 +297,7 @@ App::post('/v1/functions')
|
||||
if (!empty($providerRepositoryId)) {
|
||||
// Deploy VCS
|
||||
$redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
|
||||
} elseif(!$template->isEmpty()) {
|
||||
} elseif (!$template->isEmpty()) {
|
||||
// Deploy non-VCS from template
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
@@ -399,6 +401,7 @@ App::get('/v1/functions')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List functions')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'list')
|
||||
@@ -431,6 +434,12 @@ App::get('/v1/functions')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$functionId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -453,6 +462,7 @@ App::get('/v1/functions/runtimes')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List runtimes')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listRuntimes')
|
||||
@@ -486,6 +496,7 @@ App::get('/v1/functions/specifications')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List available function runtime specifications')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listSpecifications')
|
||||
@@ -522,6 +533,7 @@ App::get('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Get function')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'get')
|
||||
@@ -546,6 +558,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||
->desc('Get function usage')
|
||||
->groups(['api', 'functions', 'usage'])
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getFunctionUsage')
|
||||
@@ -650,6 +663,7 @@ App::get('/v1/functions/usage')
|
||||
->desc('Get functions usage')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getUsage')
|
||||
@@ -749,6 +763,7 @@ App::put('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Update function')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].update')
|
||||
->label('audits.event', 'function.update')
|
||||
->label('audits.resource', 'function/{response.$id}')
|
||||
@@ -951,7 +966,8 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Download deployment')
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getDeploymentDownload')
|
||||
->label('sdk.description', '/docs/references/functions/get-deployment-download.md')
|
||||
@@ -987,7 +1003,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||
|
||||
$response
|
||||
->setContentType('application/gzip')
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"');
|
||||
|
||||
@@ -1036,6 +1052,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Update deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
||||
->label('audits.event', 'deployment.update')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
@@ -1098,6 +1115,7 @@ App::delete('/v1/functions/:functionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Delete function')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].delete')
|
||||
->label('audits.event', 'function.delete')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
@@ -1145,12 +1163,14 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Create deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
|
||||
->label('audits.event', 'deployment.create')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'createDeployment')
|
||||
->label('sdk.methodType', 'upload')
|
||||
->label('sdk.description', '/docs/references/functions/create-deployment.md')
|
||||
->label('sdk.packaging', true)
|
||||
->label('sdk.request.type', 'multipart/form-data')
|
||||
@@ -1363,6 +1383,7 @@ App::get('/v1/functions/:functionId/deployments')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List deployments')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listDeployments')
|
||||
@@ -1406,6 +1427,12 @@ App::get('/v1/functions/:functionId/deployments')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$deploymentId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
@@ -1440,6 +1467,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Get deployment')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getDeployment')
|
||||
@@ -1483,6 +1511,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Delete deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].delete')
|
||||
->label('audits.event', 'deployment.delete')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
@@ -1548,6 +1577,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Rebuild deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
||||
->label('audits.event', 'deployment.update')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
@@ -1579,7 +1609,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
}
|
||||
|
||||
$path = $deployment->getAttribute('path');
|
||||
if(empty($path) || !$deviceForFunctions->exists($path)) {
|
||||
if (empty($path) || !$deviceForFunctions->exists($path)) {
|
||||
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -1616,6 +1646,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Cancel deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('audits.event', 'deployment.update')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -1705,7 +1736,9 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Create execution')
|
||||
->label('scope', 'execution.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].executions.[executionId].create')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'createExecution')
|
||||
@@ -1716,7 +1749,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->label('sdk.request.type', Response::CONTENT_TYPE_JSON)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
|
||||
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
|
||||
->param('async', false, new Boolean(true), 'Execute code in the background. Default value is false.', true)
|
||||
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
|
||||
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
|
||||
->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true)
|
||||
@@ -1731,9 +1764,10 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('geodb')
|
||||
->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
|
||||
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
|
||||
$async = \strval($async) === 'true' || \strval($async) === '1';
|
||||
|
||||
if(!$async && !is_null($scheduledAt)) {
|
||||
if (!$async && !is_null($scheduledAt)) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.');
|
||||
}
|
||||
|
||||
@@ -1866,7 +1900,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
|
||||
$status = $async ? 'waiting' : 'processing';
|
||||
|
||||
if(!is_null($scheduledAt)) {
|
||||
if (!is_null($scheduledAt)) {
|
||||
$status = 'scheduled';
|
||||
}
|
||||
|
||||
@@ -1896,7 +1930,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->setContext('function', $function);
|
||||
|
||||
if ($async) {
|
||||
if(is_null($scheduledAt)) {
|
||||
if (is_null($scheduledAt)) {
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
$queueForFunctions
|
||||
->setType('http')
|
||||
@@ -1918,7 +1952,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
'path' => $path,
|
||||
'method' => $method,
|
||||
'body' => $body,
|
||||
'jwt' => $jwt,
|
||||
'userId' => $user->getId()
|
||||
];
|
||||
|
||||
$schedule = $dbForConsole->createDocument('schedules', new Document([
|
||||
@@ -1987,6 +2021,19 @@ App::post('/v1/functions/:functionId/executions')
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_ID' => $deployment->getAttribute('providerRepositoryId', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment->getAttribute('providerRepositoryName', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment->getAttribute('providerRepositoryOwner', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_URL' => $deployment->getAttribute('providerRepositoryUrl', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment->getAttribute('providerBranch', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment->getAttribute('providerBranchUrl', ''),
|
||||
'APPWRITE_VCS_COMMIT_HASH' => $deployment->getAttribute('providerCommitHash', ''),
|
||||
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment->getAttribute('providerCommitMessage', ''),
|
||||
'APPWRITE_VCS_COMMIT_URL' => $deployment->getAttribute('providerCommitUrl', ''),
|
||||
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment->getAttribute('providerCommitAuthor', ''),
|
||||
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment->getAttribute('providerCommitAuthorUrl', ''),
|
||||
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment->getAttribute('providerRootDirectory', ''),
|
||||
]);
|
||||
|
||||
/** Execute function */
|
||||
@@ -2075,7 +2122,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
|
||||
$acceptTypes = \explode(', ', $request->getHeader('accept'));
|
||||
foreach ($acceptTypes as $acceptType) {
|
||||
if(\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
|
||||
if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
|
||||
$response->setContentType(Response::CONTENT_TYPE_JSON);
|
||||
break;
|
||||
} elseif (\str_starts_with($acceptType, 'multipart/form-data') || \str_starts_with($acceptType, 'multipart/*')) {
|
||||
@@ -2093,6 +2140,7 @@ App::get('/v1/functions/:functionId/executions')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('List executions')
|
||||
->label('scope', 'execution.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listExecutions')
|
||||
@@ -2138,6 +2186,12 @@ App::get('/v1/functions/:functionId/executions')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$executionId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('executions', $executionId);
|
||||
|
||||
@@ -2174,6 +2228,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Get execution')
|
||||
->label('scope', 'execution.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getExecution')
|
||||
@@ -2221,6 +2276,7 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Delete execution')
|
||||
->label('scope', 'execution.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('event', 'functions.[functionId].executions.[executionId].delete')
|
||||
->label('audits.event', 'executions.delete')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
@@ -2291,6 +2347,7 @@ App::post('/v1/functions/:functionId/variables')
|
||||
->desc('Create variable')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('audits.event', 'variable.create')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -2355,6 +2412,7 @@ App::get('/v1/functions/:functionId/variables')
|
||||
->desc('List variables')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listVariables')
|
||||
@@ -2382,6 +2440,7 @@ App::get('/v1/functions/:functionId/variables/:variableId')
|
||||
->desc('Get variable')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getVariable')
|
||||
@@ -2421,6 +2480,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||
->desc('Update variable')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('audits.event', 'variable.update')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -2482,6 +2542,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
||||
->desc('Delete variable')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', 'functions')
|
||||
->label('audits.event', 'variable.delete')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -2530,8 +2591,10 @@ App::get('/v1/functions/templates')
|
||||
->groups(['api'])
|
||||
->desc('List function templates')
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'listTemplates')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.description', '/docs/references/functions/list-templates.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
@@ -2566,8 +2629,10 @@ App::get('/v1/functions/templates')
|
||||
App::get('/v1/functions/templates/:templateId')
|
||||
->desc('Get function template')
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', 'functions')
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getTemplate')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.description', '/docs/references/functions/get-template.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
|
||||
@@ -63,13 +63,13 @@ App::get('/v1/locale')
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'public, max-age=' . $time)
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
;
|
||||
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/codes')
|
||||
->desc('List Locale Codes')
|
||||
->desc('List locale codes')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
|
||||
@@ -32,6 +32,7 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
@@ -55,6 +56,7 @@ App::post('/v1/messaging/providers/mailgun')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createMailgunProvider')
|
||||
@@ -142,6 +144,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createSendgridProvider')
|
||||
@@ -217,6 +220,7 @@ App::post('/v1/messaging/providers/smtp')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createSmtpProvider')
|
||||
@@ -304,6 +308,7 @@ App::post('/v1/messaging/providers/msg91')
|
||||
->label('audits.event', 'provider.create')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
@@ -381,6 +386,7 @@ App::post('/v1/messaging/providers/telesign')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createTelesignProvider')
|
||||
@@ -458,6 +464,7 @@ App::post('/v1/messaging/providers/textmagic')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createTextmagicProvider')
|
||||
@@ -535,6 +542,7 @@ App::post('/v1/messaging/providers/twilio')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createTwilioProvider')
|
||||
@@ -612,6 +620,7 @@ App::post('/v1/messaging/providers/vonage')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createVonageProvider')
|
||||
@@ -689,6 +698,7 @@ App::post('/v1/messaging/providers/fcm')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createFcmProvider')
|
||||
@@ -752,6 +762,7 @@ App::post('/v1/messaging/providers/apns')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createApnsProvider')
|
||||
@@ -835,6 +846,7 @@ App::get('/v1/messaging/providers')
|
||||
->desc('List providers')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listProviders')
|
||||
@@ -866,6 +878,11 @@ App::get('/v1/messaging/providers')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$providerId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
|
||||
|
||||
@@ -886,6 +903,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
->desc('List provider logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listProviderLogs')
|
||||
@@ -974,6 +992,7 @@ App::get('/v1/messaging/providers/:providerId')
|
||||
->desc('Get provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'getProvider')
|
||||
@@ -1001,6 +1020,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateMailgunProvider')
|
||||
@@ -1107,6 +1127,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateSendgridProvider')
|
||||
@@ -1198,6 +1219,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateSmtpProvider')
|
||||
@@ -1320,6 +1342,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateMsg91Provider')
|
||||
@@ -1400,6 +1423,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateTelesignProvider')
|
||||
@@ -1482,6 +1506,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateTextmagicProvider')
|
||||
@@ -1564,6 +1589,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateTwilioProvider')
|
||||
@@ -1646,6 +1672,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateVonageProvider')
|
||||
@@ -1728,6 +1755,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateFcmProvider')
|
||||
@@ -1797,6 +1825,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
||||
->label('audits.resource', 'provider/{response.$id}')
|
||||
->label('event', 'providers.[providerId].update')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateApnsProvider')
|
||||
@@ -1892,6 +1921,7 @@ App::delete('/v1/messaging/providers/:providerId')
|
||||
->label('audits.resource', 'provider/{request.$providerId}')
|
||||
->label('event', 'providers.[providerId].delete')
|
||||
->label('scope', 'providers.write')
|
||||
->label('resourceType', 'providers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'deleteProvider')
|
||||
@@ -1927,6 +1957,7 @@ App::post('/v1/messaging/topics')
|
||||
->label('audits.resource', 'topic/{response.$id}')
|
||||
->label('event', 'topics.[topicId].create')
|
||||
->label('scope', 'topics.write')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createTopic')
|
||||
@@ -1967,6 +1998,7 @@ App::get('/v1/messaging/topics')
|
||||
->desc('List topics')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listTopics')
|
||||
@@ -1998,6 +2030,11 @@ App::get('/v1/messaging/topics')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$topicId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
|
||||
|
||||
@@ -2018,6 +2055,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
->desc('List topic logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listTopicLogs')
|
||||
@@ -2107,6 +2145,7 @@ App::get('/v1/messaging/topics/:topicId')
|
||||
->desc('Get topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'getTopic')
|
||||
@@ -2135,6 +2174,7 @@ App::patch('/v1/messaging/topics/:topicId')
|
||||
->label('audits.resource', 'topic/{response.$id}')
|
||||
->label('event', 'topics.[topicId].update')
|
||||
->label('scope', 'topics.write')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateTopic')
|
||||
@@ -2179,6 +2219,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
||||
->label('audits.resource', 'topic/{request.$topicId}')
|
||||
->label('event', 'topics.[topicId].delete')
|
||||
->label('scope', 'topics.write')
|
||||
->label('resourceType', 'topics')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'deleteTopic')
|
||||
@@ -2219,6 +2260,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->label('audits.resource', 'subscriber/{response.$id}')
|
||||
->label('event', 'topics.[topicId].subscribers.[subscriberId].create')
|
||||
->label('scope', 'subscribers.write')
|
||||
->label('resourceType', 'subscribers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_JWT, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createSubscriber')
|
||||
@@ -2312,6 +2354,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('List subscribers')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('resourceType', 'subscribers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listSubscribers')
|
||||
@@ -2352,6 +2395,11 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$subscriberId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
|
||||
|
||||
@@ -2386,6 +2434,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
->desc('List subscriber logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('resourceType', 'subscribers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listSubscriberLogs')
|
||||
@@ -2475,6 +2524,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Get subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
->label('resourceType', 'subscribers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'getSubscriber')
|
||||
@@ -2517,6 +2567,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->label('audits.resource', 'subscriber/{request.$subscriberId}')
|
||||
->label('event', 'topics.[topicId].subscribers.[subscriberId].delete')
|
||||
->label('scope', 'subscribers.write')
|
||||
->label('resourceType', 'subscribers')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_JWT, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'deleteSubscriber')
|
||||
@@ -2576,6 +2627,7 @@ App::post('/v1/messaging/messages/email')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].create')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createEmail')
|
||||
@@ -2728,6 +2780,7 @@ App::post('/v1/messaging/messages/sms')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].create')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createSms')
|
||||
@@ -2844,6 +2897,7 @@ App::post('/v1/messaging/messages/push')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].create')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'createPush')
|
||||
@@ -3017,6 +3071,7 @@ App::get('/v1/messaging/messages')
|
||||
->desc('List messages')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listMessages')
|
||||
@@ -3048,6 +3103,11 @@ App::get('/v1/messaging/messages')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$messageId = $cursor->getValue();
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
|
||||
|
||||
@@ -3068,6 +3128,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
->desc('List message logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listMessageLogs')
|
||||
@@ -3157,6 +3218,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
->desc('List message targets')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'listTargets')
|
||||
@@ -3202,6 +3264,11 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$targetId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
|
||||
|
||||
@@ -3222,6 +3289,7 @@ App::get('/v1/messaging/messages/:messageId')
|
||||
->desc('Get message')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'getMessage')
|
||||
@@ -3249,6 +3317,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].update')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateEmail')
|
||||
@@ -3449,6 +3518,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].update')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updateSms')
|
||||
@@ -3604,6 +3674,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
->label('audits.resource', 'message/{response.$id}')
|
||||
->label('event', 'messages.[messageId].update')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'updatePush')
|
||||
@@ -3842,6 +3913,7 @@ App::delete('/v1/messaging/messages/:messageId')
|
||||
->label('audits.resource', 'message/{request.messageId}')
|
||||
->label('event', 'messages.[messageId].delete')
|
||||
->label('scope', 'messages.write')
|
||||
->label('resourceType', 'messages')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'messaging')
|
||||
->label('sdk.method', 'delete')
|
||||
|
||||
@@ -16,6 +16,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Migration\Sources\Appwrite;
|
||||
use Utopia\Migration\Sources\Firebase;
|
||||
@@ -33,7 +34,7 @@ include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
App::post('/v1/migrations/appwrite')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Appwrite Data')
|
||||
->desc('Migrate Appwrite data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
@@ -60,6 +61,7 @@ App::post('/v1/migrations/appwrite')
|
||||
'status' => 'pending',
|
||||
'stage' => 'init',
|
||||
'source' => Appwrite::getName(),
|
||||
'destination' => Appwrite::getName(),
|
||||
'credentials' => [
|
||||
'endpoint' => $endpoint,
|
||||
'projectId' => $projectId,
|
||||
@@ -87,7 +89,7 @@ App::post('/v1/migrations/appwrite')
|
||||
|
||||
App::post('/v1/migrations/firebase/oauth')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Firebase Data (OAuth)')
|
||||
->desc('Migrate Firebase data (OAuth)')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
@@ -164,6 +166,7 @@ App::post('/v1/migrations/firebase/oauth')
|
||||
'status' => 'pending',
|
||||
'stage' => 'init',
|
||||
'source' => Firebase::getName(),
|
||||
'destination' => Appwrite::getName(),
|
||||
'credentials' => [
|
||||
'serviceAccount' => json_encode($serviceAccount),
|
||||
],
|
||||
@@ -189,7 +192,7 @@ App::post('/v1/migrations/firebase/oauth')
|
||||
|
||||
App::post('/v1/migrations/firebase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Firebase Data (Service Account)')
|
||||
->desc('Migrate Firebase data (Service Account)')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
@@ -224,6 +227,7 @@ App::post('/v1/migrations/firebase')
|
||||
'status' => 'pending',
|
||||
'stage' => 'init',
|
||||
'source' => Firebase::getName(),
|
||||
'destination' => Appwrite::getName(),
|
||||
'credentials' => [
|
||||
'serviceAccount' => $serviceAccount,
|
||||
],
|
||||
@@ -249,7 +253,7 @@ App::post('/v1/migrations/firebase')
|
||||
|
||||
App::post('/v1/migrations/supabase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Supabase Data')
|
||||
->desc('Migrate Supabase data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
@@ -279,6 +283,7 @@ App::post('/v1/migrations/supabase')
|
||||
'status' => 'pending',
|
||||
'stage' => 'init',
|
||||
'source' => Supabase::getName(),
|
||||
'destination' => Appwrite::getName(),
|
||||
'credentials' => [
|
||||
'endpoint' => $endpoint,
|
||||
'apiKey' => $apiKey,
|
||||
@@ -309,7 +314,7 @@ App::post('/v1/migrations/supabase')
|
||||
|
||||
App::post('/v1/migrations/nhost')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate NHost Data')
|
||||
->desc('Migrate NHost data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
@@ -340,6 +345,7 @@ App::post('/v1/migrations/nhost')
|
||||
'status' => 'pending',
|
||||
'stage' => 'init',
|
||||
'source' => NHost::getName(),
|
||||
'destination' => Appwrite::getName(),
|
||||
'credentials' => [
|
||||
'subdomain' => $subdomain,
|
||||
'region' => $region,
|
||||
@@ -371,7 +377,7 @@ App::post('/v1/migrations/nhost')
|
||||
|
||||
App::get('/v1/migrations')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('List Migrations')
|
||||
->desc('List migrations')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
@@ -404,6 +410,12 @@ App::get('/v1/migrations')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$migrationId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('migrations', $migrationId);
|
||||
|
||||
@@ -424,7 +436,7 @@ App::get('/v1/migrations')
|
||||
|
||||
App::get('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Migration')
|
||||
->desc('Get migration')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
@@ -448,7 +460,7 @@ App::get('/v1/migrations/:migrationId')
|
||||
|
||||
App::get('/v1/migrations/appwrite/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Appwrite Data')
|
||||
->desc('Generate a report on Appwrite data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
@@ -490,7 +502,7 @@ App::get('/v1/migrations/appwrite/report')
|
||||
|
||||
App::get('/v1/migrations/firebase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Firebase Data')
|
||||
->desc('Generate a report on Firebase data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
@@ -537,7 +549,7 @@ App::get('/v1/migrations/firebase/report')
|
||||
|
||||
App::get('/v1/migrations/firebase/report/oauth')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Firebase Data using OAuth')
|
||||
->desc('Generate a report on Firebase data using OAuth')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'migrations')
|
||||
@@ -627,7 +639,7 @@ App::get('/v1/migrations/firebase/report/oauth')
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/firebase/connect')
|
||||
->desc('Authorize with firebase')
|
||||
->desc('Authorize with Firebase')
|
||||
->groups(['api', 'migrations'])
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -781,7 +793,7 @@ App::get('/v1/migrations/firebase/redirect')
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/firebase/projects')
|
||||
->desc('List Firebase Projects')
|
||||
->desc('List Firebase projects')
|
||||
->groups(['api', 'migrations'])
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -870,7 +882,7 @@ App::get('/v1/migrations/firebase/projects')
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/firebase/deauthorize')
|
||||
->desc('Revoke Appwrite\'s authorization to access Firebase Projects')
|
||||
->desc('Revoke Appwrite\'s authorization to access Firebase projects')
|
||||
->groups(['api', 'migrations'])
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -985,7 +997,7 @@ App::get('/v1/migrations/nhost/report')
|
||||
|
||||
App::patch('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Retry Migration')
|
||||
->desc('Retry migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].retry')
|
||||
->label('audits.event', 'migration.retry')
|
||||
@@ -1030,7 +1042,7 @@ App::patch('/v1/migrations/:migrationId')
|
||||
|
||||
App::delete('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Delete Migration')
|
||||
->desc('Delete migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].delete')
|
||||
->label('audits.event', 'migrationId.delete')
|
||||
|
||||
@@ -47,7 +47,9 @@ App::get('/v1/project/usage')
|
||||
METRIC_USERS,
|
||||
METRIC_BUCKETS,
|
||||
METRIC_FILES_STORAGE,
|
||||
METRIC_DEPLOYMENTS_STORAGE
|
||||
METRIC_DATABASES_STORAGE,
|
||||
METRIC_DEPLOYMENTS_STORAGE,
|
||||
METRIC_BUILDS_STORAGE
|
||||
],
|
||||
'period' => [
|
||||
METRIC_NETWORK_REQUESTS,
|
||||
@@ -55,6 +57,7 @@ App::get('/v1/project/usage')
|
||||
METRIC_NETWORK_OUTBOUND,
|
||||
METRIC_USERS,
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_DATABASES_STORAGE,
|
||||
METRIC_EXECUTIONS_MB_SECONDS,
|
||||
METRIC_BUILDS_MB_SECONDS
|
||||
]
|
||||
@@ -181,10 +184,11 @@ App::get('/v1/project/usage')
|
||||
];
|
||||
}, $dbForProject->find('buckets'));
|
||||
|
||||
$deploymentsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE);
|
||||
$databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) {
|
||||
$id = $database->getId();
|
||||
$name = $database->getAttribute('name');
|
||||
$metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE);
|
||||
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -195,6 +199,30 @@ App::get('/v1/project/usage')
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('databases'));
|
||||
|
||||
$functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE);
|
||||
$deploymentValue = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$deploymentMetric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
$buildMetric = str_replace(['{functionInternalId}'], [$function->getInternalId()], METRIC_FUNCTION_ID_BUILDS_STORAGE);
|
||||
$buildValue = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$buildMetric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
$value = ($buildValue['value'] ?? 0) + ($deploymentValue['value'] ?? 0);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
@@ -260,24 +288,28 @@ App::get('/v1/project/usage')
|
||||
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
|
||||
'documentsTotal' => $total[METRIC_DOCUMENTS],
|
||||
'databasesTotal' => $total[METRIC_DATABASES],
|
||||
'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE],
|
||||
'usersTotal' => $total[METRIC_USERS],
|
||||
'bucketsTotal' => $total[METRIC_BUCKETS],
|
||||
'filesStorageTotal' => $total[METRIC_FILES_STORAGE],
|
||||
'functionsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE] + $total[METRIC_BUILDS_STORAGE],
|
||||
'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE],
|
||||
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
|
||||
'executionsBreakdown' => $executionsBreakdown,
|
||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
||||
'bucketsBreakdown' => $bucketsBreakdown,
|
||||
'databasesStorageBreakdown' => $databasesStorageBreakdown,
|
||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
||||
'deploymentsStorageBreakdown' => $deploymentsStorageBreakdown,
|
||||
'functionsStorageBreakdown' => $functionsStorageBreakdown,
|
||||
]), Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
|
||||
|
||||
// Variables
|
||||
App::post('/v1/project/variables')
|
||||
->desc('Create Variable')
|
||||
->desc('Create variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('audits.event', 'variable.create')
|
||||
@@ -332,7 +364,7 @@ App::post('/v1/project/variables')
|
||||
});
|
||||
|
||||
App::get('/v1/project/variables')
|
||||
->desc('List Variables')
|
||||
->desc('List variables')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -357,7 +389,7 @@ App::get('/v1/project/variables')
|
||||
});
|
||||
|
||||
App::get('/v1/project/variables/:variableId')
|
||||
->desc('Get Variable')
|
||||
->desc('Get variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -381,7 +413,7 @@ App::get('/v1/project/variables/:variableId')
|
||||
});
|
||||
|
||||
App::put('/v1/project/variables/:variableId')
|
||||
->desc('Update Variable')
|
||||
->desc('Update variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
@@ -427,7 +459,7 @@ App::put('/v1/project/variables/:variableId')
|
||||
});
|
||||
|
||||
App::delete('/v1/project/variables/:variableId')
|
||||
->desc('Delete Variable')
|
||||
->desc('Delete variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
|
||||
@@ -16,7 +16,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
|
||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -31,6 +31,7 @@ use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
@@ -209,7 +210,7 @@ App::post('/v1/projects')
|
||||
$audit = new Audit($dbForProject);
|
||||
$audit->setup();
|
||||
|
||||
$abuse = new AbuseDatabase('', 0, 1, $dbForProject);
|
||||
$abuse = new TimeLimit('', 0, 1, $dbForProject);
|
||||
$abuse->setup();
|
||||
|
||||
/** @var array $collections */
|
||||
@@ -279,6 +280,12 @@ App::get('/v1/projects')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$projectId = $cursor->getValue();
|
||||
$cursorDocument = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
@@ -923,6 +930,7 @@ App::delete('/v1/projects/:projectId')
|
||||
}
|
||||
|
||||
$queueForDeletes
|
||||
->setProject($project)
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($project);
|
||||
|
||||
@@ -1199,7 +1207,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
App::post('/v1/projects/:projectId/keys')
|
||||
->desc('Create key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'createKey')
|
||||
@@ -1249,7 +1257,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
App::get('/v1/projects/:projectId/keys')
|
||||
->desc('List keys')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'listKeys')
|
||||
@@ -1281,7 +1289,7 @@ App::get('/v1/projects/:projectId/keys')
|
||||
App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Get key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'getKey')
|
||||
@@ -1315,7 +1323,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Update key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'updateKey')
|
||||
@@ -1361,7 +1369,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Delete key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'deleteKey')
|
||||
@@ -1436,7 +1444,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
->desc('Create platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.create')
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'createPlatform')
|
||||
@@ -1444,7 +1452,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PLATFORM)
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.')
|
||||
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY, Origin::CLIENT_TYPE_REACT_NATIVE_IOS, Origin::CLIENT_TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
|
||||
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
|
||||
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
|
||||
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
|
||||
@@ -1486,7 +1494,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
App::get('/v1/projects/:projectId/platforms')
|
||||
->desc('List platforms')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'listPlatforms')
|
||||
@@ -1518,7 +1526,7 @@ App::get('/v1/projects/:projectId/platforms')
|
||||
App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Get platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'getPlatform')
|
||||
@@ -1552,7 +1560,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Update platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'updatePlatform')
|
||||
@@ -1600,7 +1608,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Delete platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.delete')
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'deletePlatform')
|
||||
@@ -1744,7 +1752,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||
->param('port', 587, new Integer(), 'SMTP server port', true)
|
||||
->param('username', '', new Text(0, 0), 'SMTP server username', true)
|
||||
->param('password', '', new Text(0, 0), 'SMTP server password', true)
|
||||
->param('secure', '', new WhiteList(['tls'], true), 'Does SMTP server use secure connection', true)
|
||||
->param('secure', '', new WhiteList(['tls', 'ssl'], true), 'Does SMTP server use secure connection', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->inject('queueForMails')
|
||||
|
||||
@@ -13,6 +13,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Logger\Log;
|
||||
@@ -23,7 +24,7 @@ use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/proxy/rules')
|
||||
->groups(['api', 'proxy'])
|
||||
->desc('Create Rule')
|
||||
->desc('Create rule')
|
||||
->label('scope', 'rules.write')
|
||||
->label('event', 'rules.[ruleId].create')
|
||||
->label('audits.event', 'rule.create')
|
||||
@@ -149,7 +150,7 @@ App::post('/v1/proxy/rules')
|
||||
|
||||
App::get('/v1/proxy/rules')
|
||||
->groups(['api', 'proxy'])
|
||||
->desc('List Rules')
|
||||
->desc('List rules')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'proxy')
|
||||
@@ -185,6 +186,12 @@ App::get('/v1/proxy/rules')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$ruleId = $cursor->getValue();
|
||||
$cursorDocument = $dbForConsole->getDocument('rules', $ruleId);
|
||||
|
||||
@@ -212,7 +219,7 @@ App::get('/v1/proxy/rules')
|
||||
|
||||
App::get('/v1/proxy/rules/:ruleId')
|
||||
->groups(['api', 'proxy'])
|
||||
->desc('Get Rule')
|
||||
->desc('Get rule')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'proxy')
|
||||
@@ -241,7 +248,7 @@ App::get('/v1/proxy/rules/:ruleId')
|
||||
|
||||
App::delete('/v1/proxy/rules/:ruleId')
|
||||
->groups(['api', 'proxy'])
|
||||
->desc('Delete Rule')
|
||||
->desc('Delete rule')
|
||||
->label('scope', 'rules.write')
|
||||
->label('event', 'rules.[ruleId].delete')
|
||||
->label('audits.event', 'rules.delete')
|
||||
@@ -277,7 +284,7 @@ App::delete('/v1/proxy/rules/:ruleId')
|
||||
});
|
||||
|
||||
App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
->desc('Update Rule Verification Status')
|
||||
->desc('Update rule verification status')
|
||||
->groups(['api', 'proxy'])
|
||||
->label('scope', 'rules.write')
|
||||
->label('event', 'rules.[ruleId].update')
|
||||
|
||||
@@ -24,6 +24,7 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Image\Image;
|
||||
use Utopia\Storage\Compression\Algorithms\GZIP;
|
||||
@@ -48,6 +49,7 @@ App::post('/v1/storage/buckets')
|
||||
->desc('Create bucket')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('event', 'buckets.[bucketId].create')
|
||||
->label('audits.event', 'bucket.create')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
@@ -146,6 +148,7 @@ App::get('/v1/storage/buckets')
|
||||
->desc('List buckets')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'listBuckets')
|
||||
@@ -178,6 +181,12 @@ App::get('/v1/storage/buckets')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$bucketId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('buckets', $bucketId);
|
||||
|
||||
@@ -200,6 +209,7 @@ App::get('/v1/storage/buckets/:bucketId')
|
||||
->desc('Get bucket')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getBucket')
|
||||
@@ -225,6 +235,7 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->desc('Update bucket')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('event', 'buckets.[bucketId].update')
|
||||
->label('audits.event', 'bucket.update')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
@@ -292,6 +303,7 @@ App::delete('/v1/storage/buckets/:bucketId')
|
||||
->desc('Delete bucket')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'buckets.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('audits.event', 'bucket.delete')
|
||||
->label('event', 'buckets.[bucketId].delete')
|
||||
->label('audits.resource', 'bucket/{request.bucketId}')
|
||||
@@ -334,6 +346,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
->desc('Create file')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('audits.event', 'file.create')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].create')
|
||||
->label('audits.resource', 'file/{response.$id}')
|
||||
@@ -695,6 +708,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->desc('List files')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'listFiles')
|
||||
@@ -744,6 +758,12 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$fileId = $cursor->getValue();
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
@@ -780,6 +800,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->desc('Get file')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFile')
|
||||
@@ -827,6 +848,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->desc('Get file preview')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('cache', true)
|
||||
->label('cache.resourceType', 'bucket/{request.bucketId}')
|
||||
->label('cache.resource', 'file/{request.fileId}')
|
||||
@@ -889,10 +911,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' === $output)) { // Fallback webp to jpeg when no browser support
|
||||
$output = 'jpg';
|
||||
}
|
||||
|
||||
$inputs = Config::getParam('storage-inputs');
|
||||
$outputs = Config::getParam('storage-outputs');
|
||||
$fileLogos = Config::getParam('storage-logos');
|
||||
@@ -990,7 +1008,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType($contentType)
|
||||
->file($data)
|
||||
;
|
||||
@@ -1003,6 +1021,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->desc('Get file for download')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileDownload')
|
||||
@@ -1053,7 +1072,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
|
||||
$response
|
||||
->setContentType($file->getAttribute('mimeType'))
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
|
||||
;
|
||||
@@ -1143,6 +1162,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->desc('Get file for view')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('cache', true)
|
||||
->label('cache.resourceType', 'bucket/{request.bucketId}')
|
||||
->label('cache.resource', 'file/{request.fileId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileView')
|
||||
@@ -1203,7 +1226,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
;
|
||||
|
||||
@@ -1262,20 +1285,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
|
||||
if (!empty($source)) {
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send(substr($source, $start, ($end - $start + 1)));
|
||||
$response->file(substr($source, $start, ($end - $start + 1)));
|
||||
}
|
||||
$response->send($source);
|
||||
$response->file($source);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send($deviceForFiles->read($path, $start, ($end - $start + 1)));
|
||||
$response->file($deviceForFiles->read($path, $start, ($end - $start + 1)));
|
||||
return;
|
||||
}
|
||||
|
||||
$size = $deviceForFiles->getFileSize($path);
|
||||
if ($size > APP_STORAGE_READ_BUFFER) {
|
||||
for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) {
|
||||
|
||||
$response->chunk(
|
||||
$deviceForFiles->read(
|
||||
$path,
|
||||
@@ -1286,7 +1310,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$response->send($deviceForFiles->read($path));
|
||||
$response->file($deviceForFiles->read($path));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1294,6 +1318,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->desc('Get file for push notification')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', '*/*')
|
||||
->label('sdk.methodType', 'location')
|
||||
@@ -1357,7 +1382,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage());
|
||||
|
||||
$size = $file->getAttribute('sizeOriginal', 0);
|
||||
@@ -1448,6 +1473,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->desc('Update file')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].update')
|
||||
->label('audits.event', 'file.update')
|
||||
->label('audits.resource', 'file/{response.$id}')
|
||||
@@ -1549,9 +1575,10 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
});
|
||||
|
||||
App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->desc('Delete File')
|
||||
->desc('Delete file')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.write')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('event', 'buckets.[bucketId].files.[fileId].delete')
|
||||
->label('audits.event', 'file.delete')
|
||||
->label('audits.resource', 'file/{request.fileId}')
|
||||
@@ -1645,6 +1672,7 @@ App::get('/v1/storage/usage')
|
||||
->desc('Get storage usage stats')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getUsage')
|
||||
@@ -1724,6 +1752,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
->desc('Get bucket usage stats')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.read')
|
||||
->label('resourceType', 'buckets')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getBucketUsage')
|
||||
|
||||
@@ -34,6 +34,7 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -43,6 +44,7 @@ use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/teams')
|
||||
->desc('Create team')
|
||||
@@ -169,6 +171,12 @@ App::get('/v1/teams')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$teamId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
@@ -394,7 +402,17 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->param('email', '', new Email(), 'Email of the new team member.', true)
|
||||
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
|
||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
|
||||
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
|
||||
->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]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -740,6 +758,13 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
|
||||
$membershipId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
@@ -868,7 +893,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
|
||||
->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]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
||||
@@ -36,6 +36,7 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -140,7 +141,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
$existingTarget = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$email]),
|
||||
]);
|
||||
if($existingTarget) {
|
||||
if ($existingTarget) {
|
||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -164,7 +165,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
$existingTarget = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$phone]),
|
||||
]);
|
||||
if($existingTarget) {
|
||||
if ($existingTarget) {
|
||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -452,7 +453,7 @@ App::post('/v1/users/scrypt-modified')
|
||||
});
|
||||
|
||||
App::post('/v1/users/:userId/targets')
|
||||
->desc('Create User Target')
|
||||
->desc('Create user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.create')
|
||||
->label('audits.resource', 'target/response.$id')
|
||||
@@ -576,6 +577,12 @@ App::get('/v1/users')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$userId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
@@ -647,7 +654,7 @@ App::get('/v1/users/:userId/prefs')
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Get User Target')
|
||||
->desc('Get user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||
@@ -848,7 +855,7 @@ App::get('/v1/users/:userId/logs')
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/targets')
|
||||
->desc('List User Targets')
|
||||
->desc('List user targets')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
|
||||
@@ -886,6 +893,11 @@ App::get('/v1/users/:userId/targets')
|
||||
$cursor = reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$targetId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
|
||||
|
||||
@@ -903,7 +915,7 @@ App::get('/v1/users/:userId/targets')
|
||||
});
|
||||
|
||||
App::get('/v1/users/identities')
|
||||
->desc('List Identities')
|
||||
->desc('List identities')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
@@ -938,6 +950,12 @@ App::get('/v1/users/identities')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$identityId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('identities', $identityId);
|
||||
|
||||
@@ -1425,7 +1443,7 @@ App::patch('/v1/users/:userId/prefs')
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Update User target')
|
||||
->desc('Update user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.update')
|
||||
->label('audits.resource', 'target/{response.$id}')
|
||||
@@ -1485,7 +1503,9 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||
}
|
||||
|
||||
$target->setAttribute('identifier', $identifier);
|
||||
$target
|
||||
->setAttribute('identifier', $identifier)
|
||||
->setAttribute('expired', false);
|
||||
}
|
||||
|
||||
if ($providerId) {
|
||||
@@ -1499,8 +1519,9 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
|
||||
}
|
||||
|
||||
$target->setAttribute('providerId', $provider->getId());
|
||||
$target->setAttribute('providerInternalId', $provider->getInternalId());
|
||||
$target
|
||||
->setAttribute('providerId', $provider->getId())
|
||||
->setAttribute('providerInternalId', $provider->getInternalId());
|
||||
}
|
||||
|
||||
if ($name) {
|
||||
@@ -1557,7 +1578,7 @@ App::patch('/v1/users/:userId/mfa')
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/mfa/factors')
|
||||
->desc('List Factors')
|
||||
->desc('List factors')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
@@ -1590,7 +1611,7 @@ App::get('/v1/users/:userId/mfa/factors')
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Get MFA Recovery Codes')
|
||||
->desc('Get MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
@@ -1625,7 +1646,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Create MFA Recovery Codes')
|
||||
->desc('Create MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].create.mfa.recovery-codes')
|
||||
->label('scope', 'users.write')
|
||||
@@ -1671,7 +1692,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Regenerate MFA Recovery Codes')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
||||
->label('scope', 'users.write')
|
||||
@@ -1716,7 +1737,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
->desc('Delete Authenticator')
|
||||
->desc('Delete authenticator')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].delete.mfa')
|
||||
->label('scope', 'users.write')
|
||||
@@ -2124,7 +2145,7 @@ App::post('/v1/users/:userId/jwts')
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$session = new Document();
|
||||
|
||||
if($sessionId === 'recent') {
|
||||
if ($sessionId === 'recent') {
|
||||
// Get most recent
|
||||
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Detector\Adapter\Bun;
|
||||
use Utopia\Detector\Adapter\CPP;
|
||||
use Utopia\Detector\Adapter\Dart;
|
||||
@@ -263,7 +264,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
};
|
||||
|
||||
App::get('/v1/vcs/github/authorize')
|
||||
->desc('Install GitHub App')
|
||||
->desc('Install GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk.namespace', 'vcs')
|
||||
@@ -305,7 +306,7 @@ App::get('/v1/vcs/github/authorize')
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/callback')
|
||||
->desc('Capture installation and authorization from GitHub App')
|
||||
->desc('Capture installation and authorization from GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -509,7 +510,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
$vcsContents = [];
|
||||
foreach ($contents as $content) {
|
||||
$isDirectory = false;
|
||||
if($content['type'] === GitHub::CONTENTS_DIRECTORY) {
|
||||
if ($content['type'] === GitHub::CONTENTS_DIRECTORY) {
|
||||
$isDirectory = true;
|
||||
}
|
||||
|
||||
@@ -598,7 +599,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->desc('List Repositories')
|
||||
->desc('List repositories')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk.namespace', 'vcs')
|
||||
@@ -843,7 +844,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches')
|
||||
->desc('List Repository Branches')
|
||||
->desc('List repository branches')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk.namespace', 'vcs')
|
||||
@@ -892,7 +893,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
});
|
||||
|
||||
App::post('/v1/vcs/github/events')
|
||||
->desc('Create Event')
|
||||
->desc('Create event')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->inject('gitHub')
|
||||
@@ -1069,6 +1070,12 @@ App::get('/v1/vcs/installations')
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$installationId = $cursor->getValue();
|
||||
$cursorDocument = $dbForConsole->getDocument('installations', $installationId);
|
||||
|
||||
@@ -1120,7 +1127,7 @@ App::get('/v1/vcs/installations/:installationId')
|
||||
});
|
||||
|
||||
App::delete('/v1/vcs/installations/:installationId')
|
||||
->desc('Delete Installation')
|
||||
->desc('Delete installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk.namespace', 'vcs')
|
||||
|
||||
+109
-24
@@ -6,6 +6,7 @@ use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
@@ -25,6 +26,7 @@ use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
@@ -32,6 +34,7 @@ use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Adapter\Sentry;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\User;
|
||||
use Utopia\Logger\Logger;
|
||||
@@ -43,7 +46,7 @@ Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Reader $geodb)
|
||||
function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked)
|
||||
{
|
||||
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
|
||||
|
||||
@@ -96,6 +99,9 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
$type = $route->getAttribute('resourceType');
|
||||
|
||||
if ($type === 'function') {
|
||||
$utopia->getRoute()?->label('sdk.namespace', 'functions');
|
||||
$utopia->getRoute()?->label('sdk.method', 'createExecution');
|
||||
|
||||
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||
if ($request->getProtocol() !== 'https') {
|
||||
if ($request->getMethod() !== Request::METHOD_GET) {
|
||||
@@ -131,6 +137,10 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($isResourceBlocked($project, 'functions', $functionId)) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_RESOURCE_BLOCKED);
|
||||
}
|
||||
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
@@ -271,6 +281,19 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_ID' => $deployment->getAttribute('providerRepositoryId', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment->getAttribute('providerRepositoryName', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment->getAttribute('providerRepositoryOwner', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_URL' => $deployment->getAttribute('providerRepositoryUrl', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment->getAttribute('providerBranch', ''),
|
||||
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment->getAttribute('providerBranchUrl', ''),
|
||||
'APPWRITE_VCS_COMMIT_HASH' => $deployment->getAttribute('providerCommitHash', ''),
|
||||
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment->getAttribute('providerCommitMessage', ''),
|
||||
'APPWRITE_VCS_COMMIT_URL' => $deployment->getAttribute('providerCommitUrl', ''),
|
||||
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment->getAttribute('providerCommitAuthor', ''),
|
||||
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment->getAttribute('providerCommitAuthorUrl', ''),
|
||||
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment->getAttribute('providerRootDirectory', ''),
|
||||
]);
|
||||
|
||||
/** Execute function */
|
||||
@@ -329,7 +352,16 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
}
|
||||
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_NETWORK_REQUESTS, 1)
|
||||
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
|
||||
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
|
||||
->addMetric(METRIC_EXECUTIONS, 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
@@ -340,10 +372,11 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
->trigger()
|
||||
;
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
$queueForFunctions
|
||||
->setType(Func::TYPE_ASYNC_WRITE)
|
||||
->setExecution($execution)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
$execution->setAttribute('logs', '');
|
||||
@@ -427,7 +460,9 @@ App::init()
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForCertificates')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates) {
|
||||
->inject('queueForFunctions')
|
||||
->inject('isResourceBlocked')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -435,7 +470,7 @@ App::init()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain) {
|
||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
|
||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -643,8 +678,10 @@ App::options()
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('geodb')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
||||
->inject('isResourceBlocked')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -652,7 +689,7 @@ App::options()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain) {
|
||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
|
||||
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -754,16 +791,24 @@ App::error()
|
||||
$providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', '');
|
||||
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
|
||||
|
||||
if (!(empty($providerName) || empty($providerConfig))) {
|
||||
if (!Logger::hasProvider($providerName)) {
|
||||
throw new Exception("Logging provider not supported. Logging is disabled");
|
||||
}
|
||||
try {
|
||||
$loggingProvider = new DSN($providerConfig ?? '');
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
|
||||
$adapter = new $classname($providerConfig);
|
||||
$logger = new Logger($adapter);
|
||||
$logger->setSample(0.04);
|
||||
$publish = true;
|
||||
if (!empty($providerName) && $providerName === 'sentry') {
|
||||
$key = $loggingProvider->getPassword();
|
||||
$projectId = $loggingProvider->getUser() ?? '';
|
||||
$host = 'https://' . $loggingProvider->getHost();
|
||||
|
||||
$adapter = new Sentry($projectId, $key, $host);
|
||||
$logger = new Logger($adapter);
|
||||
$logger->setSample(0.04);
|
||||
$publish = true;
|
||||
} else {
|
||||
throw new \Exception('Invalid experimental logging provider');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning('Failed to initialize logging provider: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,8 +877,12 @@ App::error()
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Log pushed with status code: ' . $responseCode);
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Wrap all exceptions inside Appwrite\Extend\Exception */
|
||||
@@ -922,8 +971,10 @@ App::get('/robots.txt')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('geodb')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
||||
->inject('isResourceBlocked')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -931,7 +982,7 @@ App::get('/robots.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
|
||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -947,8 +998,10 @@ App::get('/humans.txt')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('geodb')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
|
||||
->inject('isResourceBlocked')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -956,7 +1009,7 @@ App::get('/humans.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
|
||||
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1013,6 +1066,38 @@ App::get('/.well-known/acme-challenge/*')
|
||||
include_once __DIR__ . '/shared/api.php';
|
||||
include_once __DIR__ . '/shared/api/auth.php';
|
||||
|
||||
App::get('/v1/ping')
|
||||
->groups(['api', 'general'])
|
||||
->desc('Test the connection between the Appwrite and the SDK.')
|
||||
->label('scope', 'global')
|
||||
->label('event', 'projects.[projectId].ping')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForConsole')
|
||||
->inject('queueForEvents')
|
||||
->action(function (Response $response, Document $project, Database $dbForConsole, Event $queueForEvents) {
|
||||
if ($project->isEmpty()) {
|
||||
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$pingCount = $project->getAttribute('pingCount', 0) + 1;
|
||||
$pingedAt = DateTime::now();
|
||||
|
||||
$project
|
||||
->setAttribute('pingCount', $pingCount)
|
||||
->setAttribute('pingedAt', $pingedAt);
|
||||
|
||||
Authorization::skip(function () use ($dbForConsole, $project) {
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
});
|
||||
|
||||
$queueForEvents
|
||||
->setParam('projectId', $project->getId())
|
||||
->setPayload($response->output($project, Response::MODEL_PROJECT));
|
||||
|
||||
$response->text('Pong!');
|
||||
});
|
||||
|
||||
App::wildcard()
|
||||
->groups(['api'])
|
||||
->label('scope', 'global')
|
||||
|
||||
@@ -158,7 +158,7 @@ App::patch('/v1/mock/functions-v2')
|
||||
App::post('/v1/mock/api-key-unprefixed')
|
||||
->desc('Create API Key (without standard prefix)')
|
||||
->groups(['mock', 'api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', new UID(), 'Project ID.')
|
||||
->inject('response')
|
||||
|
||||
@@ -18,7 +18,7 @@ use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
|
||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -95,7 +95,7 @@ $databaseListener = function (string $event, Document $document, Document $proje
|
||||
$databaseInternalId = $parts[1] ?? 0;
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_COLLECTIONS, $value) // per project
|
||||
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database
|
||||
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value)
|
||||
;
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
@@ -160,42 +160,22 @@ App::init()
|
||||
->inject('session')
|
||||
->inject('servers')
|
||||
->inject('mode')
|
||||
->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode) {
|
||||
->inject('team')
|
||||
->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) {
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* ACL Check
|
||||
*/
|
||||
/** Default role */
|
||||
$roles = Config::getParam('roles', []);
|
||||
$role = ($user->isEmpty())
|
||||
? Role::guests()->toString()
|
||||
: Role::users()->toString();
|
||||
|
||||
// Add user roles
|
||||
$memberships = $user->find('teamId', $project->getAttribute('teamId'), 'memberships');
|
||||
|
||||
if ($memberships) {
|
||||
foreach ($memberships->getAttribute('roles', []) as $memberRole) {
|
||||
switch ($memberRole) {
|
||||
case 'owner':
|
||||
$role = Auth::USER_ROLE_OWNER;
|
||||
break;
|
||||
case 'admin':
|
||||
$role = Auth::USER_ROLE_ADMIN;
|
||||
break;
|
||||
case 'developer':
|
||||
$role = Auth::USER_ROLE_DEVELOPER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$roles = Config::getParam('roles', []);
|
||||
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
|
||||
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
|
||||
/** Allowed Scopes for the role */
|
||||
$scopes = $roles[$role]['scopes'];
|
||||
|
||||
$apiKey = $request->getHeader('x-appwrite-key', '');
|
||||
|
||||
@@ -207,14 +187,14 @@ App::init()
|
||||
}
|
||||
|
||||
// Remove after migration
|
||||
if(!\str_contains($apiKey, '_')) {
|
||||
if (!\str_contains($apiKey, '_')) {
|
||||
$keyType = API_KEY_STANDARD;
|
||||
$authKey = $apiKey;
|
||||
} else {
|
||||
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
|
||||
}
|
||||
|
||||
if($keyType === API_KEY_DYNAMIC) {
|
||||
if ($keyType === API_KEY_DYNAMIC) {
|
||||
// Dynamic key
|
||||
|
||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||
@@ -244,7 +224,7 @@ App::init()
|
||||
Authorization::setRole(Auth::USER_ROLE_APPS);
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||
}
|
||||
} elseif($keyType === API_KEY_STANDARD) {
|
||||
} elseif ($keyType === API_KEY_STANDARD) {
|
||||
// No underline means no prefix. Backwards compatibility.
|
||||
// Regular key
|
||||
|
||||
@@ -294,13 +274,38 @@ App::init()
|
||||
}
|
||||
}
|
||||
}
|
||||
// Admin User Authentication
|
||||
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
|
||||
$teamId = $team->getId();
|
||||
$adminRoles = [];
|
||||
$memberships = $user->getAttribute('memberships', []);
|
||||
foreach ($memberships as $membership) {
|
||||
if ($membership->getAttribute('confirm', false) === true && $membership->getAttribute('teamId') === $teamId) {
|
||||
$adminRoles = $membership->getAttribute('roles', []);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($adminRoles)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$scopes = []; // reset scope if admin
|
||||
foreach ($adminRoles as $role) {
|
||||
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
|
||||
}
|
||||
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
}
|
||||
|
||||
$scopes = \array_unique($scopes);
|
||||
|
||||
Authorization::setRole($role);
|
||||
|
||||
foreach (Auth::getRoles($user) as $authRole) {
|
||||
Authorization::setRole($authRole);
|
||||
}
|
||||
|
||||
/** Do not allow access to disabled services */
|
||||
$service = $route->getLabel('sdk.namespace', '');
|
||||
if (!empty($service)) {
|
||||
if (
|
||||
@@ -311,14 +316,14 @@ App::init()
|
||||
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
|
||||
}
|
||||
}
|
||||
if (!\in_array($scope, $scopes)) {
|
||||
if ($project->isEmpty()) { // Check if permission is denied because project is missing
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** Do now allow access if scope is not allowed */
|
||||
$scope = $route->getLabel('scope', 'none');
|
||||
if (!\in_array($scope, $scopes)) {
|
||||
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')');
|
||||
}
|
||||
|
||||
/** Do not allow access to blocked accounts */
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception(Exception::USER_BLOCKED);
|
||||
}
|
||||
@@ -380,7 +385,7 @@ App::init()
|
||||
foreach ($abuseKeyLabel as $abuseKey) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$timeLimit = new AbuseDatabase($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
|
||||
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
|
||||
$timeLimit
|
||||
->setParam('{projectId}', $project->getId())
|
||||
->setParam('{userId}', $user->getId())
|
||||
@@ -457,7 +462,11 @@ App::init()
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
if ($useCache) {
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$keyParams = $request->getURI() . '*' . implode('*', $request->getParams());
|
||||
if(!empty($request->getRangeStart())) $keyParams .= '*'.$request->getRangeStart();
|
||||
if(!empty($request->getRangeEnd())) $keyParams .= '*'.$request->getRangeEnd();
|
||||
$keyParams .= '*' . APP_CACHE_BUSTER;
|
||||
$key = md5($keyParams);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
@@ -466,6 +475,7 @@ App::init()
|
||||
$data = $cache->load($key, $timestamp);
|
||||
|
||||
if (!empty($data) && !$cacheLog->isEmpty()) {
|
||||
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
|
||||
$type = $parts[0] ?? null;
|
||||
|
||||
@@ -501,17 +511,19 @@ App::init()
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->setContentType($cacheLog->getAttribute('mimeType'))
|
||||
->send($data);
|
||||
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
|
||||
->addHeader('X-Appwrite-Cache', 'hit');
|
||||
foreach ($cacheLog->getAttribute('headers') as $key => $value) {
|
||||
$response->addHeader($key, $value);
|
||||
}
|
||||
|
||||
$response->send($data);
|
||||
} else {
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Expires', '0')
|
||||
->addHeader('X-Appwrite-Cache', 'miss')
|
||||
;
|
||||
}
|
||||
@@ -602,10 +614,11 @@ App::shutdown()
|
||||
/**
|
||||
* Trigger functions.
|
||||
*/
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
|
||||
if (!$queueForEvents->isPaused()) {
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
}
|
||||
/**
|
||||
* Trigger webhooks.
|
||||
*/
|
||||
@@ -698,7 +711,8 @@ App::shutdown()
|
||||
if (!empty($queueForMessaging->getType())) {
|
||||
$queueForMessaging->trigger();
|
||||
}
|
||||
|
||||
var_dump($request->getRangeStart());
|
||||
var_dump($request->getRangeEnd());
|
||||
/**
|
||||
* Cache label
|
||||
*/
|
||||
@@ -706,7 +720,9 @@ App::shutdown()
|
||||
if ($useCache) {
|
||||
$resource = $resourceType = null;
|
||||
$data = $response->getPayload();
|
||||
var_dump($response->getHeaders());
|
||||
if (!empty($data['payload'])) {
|
||||
|
||||
$pattern = $route->getLabel('cache.resource', null);
|
||||
if (!empty($pattern)) {
|
||||
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
@@ -717,17 +733,30 @@ App::shutdown()
|
||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$keyParams = $request->getURI() . '*' . implode('*', $request->getParams());
|
||||
if(!empty($request->getRangeStart())) $keyParams .= '*'.$request->getRangeStart();
|
||||
if(!empty($request->getRangeEnd())) $keyParams .= '*'.$request->getRangeEnd();
|
||||
$keyParams .= '*'. APP_CACHE_BUSTER;
|
||||
|
||||
$key = md5($keyParams);
|
||||
$signature = md5($data['payload']);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||
$now = DateTime::now();
|
||||
if ($cacheLog->isEmpty()) {
|
||||
$filterList = ['Cache-Control', 'Pragma', 'Expires', 'X-Appwrite-Cache'];
|
||||
$headers = [];
|
||||
foreach ($response->getHeaders() as $name => $value) {
|
||||
if(!in_array($name, $filterList)) {
|
||||
$headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
|
||||
'$id' => $key,
|
||||
'resource' => $resource,
|
||||
'resourceType' => $resourceType,
|
||||
'mimeType' => $response->getContentType(),
|
||||
'headers' => $headers,
|
||||
'accessedAt' => $now,
|
||||
'signature' => $signature,
|
||||
])));
|
||||
@@ -745,8 +774,6 @@ App::shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($project->getId() !== 'console') {
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
$fileSize = 0;
|
||||
|
||||
@@ -32,8 +32,9 @@ App::get('/')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$url = parse_url($request->getURI());
|
||||
$target = "/console{$url['path']}";
|
||||
if ($url['query'] ?? false) {
|
||||
$target .= "?{$url['query']}";
|
||||
$params = $request->getParams();
|
||||
if (!empty($params)) {
|
||||
$target .= "?" . \http_build_query($params);
|
||||
}
|
||||
if ($url['fragment'] ?? false) {
|
||||
$target .= "#{$url['fragment']}";
|
||||
|
||||
+9
-5
@@ -9,7 +9,7 @@ use Swoole\Http\Request as SwooleRequest;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
use Swoole\Http\Server;
|
||||
use Swoole\Process;
|
||||
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
|
||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\CLI\Console;
|
||||
@@ -101,8 +101,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
if ($dbForConsole->getCollection(AbuseDatabase::COLLECTION)->isEmpty()) {
|
||||
$adapter = new AbuseDatabase("", 0, 1, $dbForConsole);
|
||||
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
|
||||
$adapter = new TimeLimit("", 0, 1, $dbForConsole);
|
||||
$adapter->setup();
|
||||
}
|
||||
|
||||
@@ -298,8 +298,12 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Log pushed with status code: ' . $responseCode);
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
|
||||
+80
-21
@@ -41,6 +41,7 @@ use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\URL\URL as AppwriteURL;
|
||||
use Appwrite\Utopia\Request;
|
||||
use MaxMind\Db\Reader;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Swoole\Database\PDOProxy;
|
||||
@@ -118,7 +119,7 @@ const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4315;
|
||||
const APP_CACHE_BUSTER = 4318;
|
||||
const APP_VERSION_STABLE = '1.6.0';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
@@ -129,6 +130,7 @@ const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000;
|
||||
const APP_DATABASE_QUERY_MAX_VALUES = 500;
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
const APP_STORAGE_BUILDS = '/storage/builds';
|
||||
@@ -148,9 +150,12 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB;
|
||||
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
|
||||
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
|
||||
const APP_FUNCTION_MEMORY_DEFAULT = 512;
|
||||
const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
@@ -223,15 +228,28 @@ const API_KEY_DYNAMIC = 'dynamic';
|
||||
// Usage metrics
|
||||
const METRIC_TEAMS = 'teams';
|
||||
const METRIC_USERS = 'users';
|
||||
const METRIC_MESSAGES = 'messages';
|
||||
const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages';
|
||||
|
||||
const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone';
|
||||
const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}';
|
||||
const METRIC_MESSAGES = 'messages';
|
||||
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
|
||||
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
|
||||
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
|
||||
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
|
||||
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
|
||||
const METRIC_SESSIONS = 'sessions';
|
||||
const METRIC_DATABASES = 'databases';
|
||||
const METRIC_COLLECTIONS = 'collections';
|
||||
const METRIC_DATABASES_STORAGE = 'databases.storage';
|
||||
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
|
||||
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
|
||||
const METRIC_DOCUMENTS = 'documents';
|
||||
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
|
||||
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
|
||||
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
|
||||
const METRIC_BUCKETS = 'buckets';
|
||||
const METRIC_FILES = 'files';
|
||||
const METRIC_FILES_STORAGE = 'files.storage';
|
||||
@@ -759,8 +777,9 @@ $register->set('logger', function () {
|
||||
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
|
||||
default => ['key' => $loggingProvider->getHost()],
|
||||
};
|
||||
} catch (Throwable) {
|
||||
} catch (Throwable $th) {
|
||||
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
|
||||
Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage());
|
||||
$configChunks = \explode(";", $providerConfig);
|
||||
|
||||
$providerConfig = match ($providerName) {
|
||||
@@ -790,7 +809,7 @@ $register->set('logger', function () {
|
||||
$adapter = null;
|
||||
}
|
||||
|
||||
if($adapter === null) {
|
||||
if ($adapter === null) {
|
||||
Console::error("Logging provider not supported. Logging is disabled");
|
||||
return;
|
||||
}
|
||||
@@ -1017,7 +1036,7 @@ $register->set('smtp', function () {
|
||||
return $mail;
|
||||
});
|
||||
$register->set('geodb', function () {
|
||||
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb');
|
||||
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb');
|
||||
});
|
||||
$register->set('passwordsDictionary', function () {
|
||||
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
|
||||
@@ -1241,13 +1260,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
$user = new Document([]);
|
||||
}
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
} else {
|
||||
$user = new Document([]);
|
||||
}
|
||||
}
|
||||
// if (APP_MODE_ADMIN === $mode) {
|
||||
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
|
||||
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
// } else {
|
||||
// $user = new Document([]);
|
||||
// }
|
||||
// }
|
||||
|
||||
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
||||
|
||||
@@ -1266,7 +1285,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
if(!empty($jwtSessionId)) {
|
||||
if (!empty($jwtSessionId)) {
|
||||
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
|
||||
$user = new Document([]);
|
||||
}
|
||||
@@ -1324,7 +1343,7 @@ App::setResource('console', function () {
|
||||
'$collection' => ID::custom('projects'),
|
||||
'description' => 'Appwrite core engine',
|
||||
'logo' => '',
|
||||
'teamId' => -1,
|
||||
'teamId' => null,
|
||||
'webhooks' => [],
|
||||
'keys' => [],
|
||||
'platforms' => [
|
||||
@@ -1380,7 +1399,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
try {
|
||||
$dsn = new DSN($project->getAttribute('database'));
|
||||
@@ -1416,7 +1436,8 @@ App::setResource('dbForConsole', function (Group $pools, Cache $cache) {
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', 'console')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'cache']);
|
||||
@@ -1440,7 +1461,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
|
||||
$database
|
||||
@@ -1505,9 +1527,9 @@ App::setResource('deviceForBuilds', function ($project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
function getDevice($root): Device
|
||||
function getDevice(string $root, string $connection = ''): Device
|
||||
{
|
||||
$connection = System::getEnv('_APP_CONNECTIONS_STORAGE', '');
|
||||
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
|
||||
|
||||
if (!empty($connection)) {
|
||||
$acl = 'private';
|
||||
@@ -1749,6 +1771,43 @@ App::setResource('requestTimestamp', function ($request) {
|
||||
}
|
||||
return $requestTimestamp;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('plan', function (array $plan = []) {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('team', function (Document $project, Database $dbForConsole, App $utopia, Request $request) {
|
||||
$teamInternalId = '';
|
||||
if ($project->getId() !== 'console') {
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', '');
|
||||
} else {
|
||||
$route = $utopia->match($request);
|
||||
$path = $route->getPath();
|
||||
if (str_starts_with($path, '/v1/projects/:projectId')) {
|
||||
$uri = $request->getURI();
|
||||
$pid = explode('/', $uri)[3];
|
||||
$p = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $pid));
|
||||
$teamInternalId = $p->getAttribute('teamInternalId', '');
|
||||
} elseif ($path === '/v1/projects') {
|
||||
$teamId = $request->getParam('teamId', '');
|
||||
$team = Authorization::skip(fn () => $dbForConsole->getDocument('teams', $teamId));
|
||||
return $team;
|
||||
}
|
||||
}
|
||||
|
||||
$team = Authorization::skip(function () use ($dbForConsole, $teamInternalId) {
|
||||
return $dbForConsole->findOne('teams', [
|
||||
Query::equal('$internalId', [$teamInternalId]),
|
||||
]);
|
||||
});
|
||||
|
||||
if (!$team) {
|
||||
$team = new Document([]);
|
||||
}
|
||||
return $team;
|
||||
}, ['project', 'dbForConsole', 'utopia', 'request']);
|
||||
|
||||
App::setResource(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
|
||||
);
|
||||
|
||||
+20
-9
@@ -13,7 +13,7 @@ use Swoole\Runtime;
|
||||
use Swoole\Table;
|
||||
use Swoole\Timer;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
|
||||
use Utopia\Abuse\Adapters\Database\TimeLimit;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -40,7 +40,7 @@ require_once __DIR__ . '/init.php';
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
||||
// Allows overriding
|
||||
if (!function_exists("getConsoleDB")) {
|
||||
if (!function_exists('getConsoleDB')) {
|
||||
function getConsoleDB(): Database
|
||||
{
|
||||
global $register;
|
||||
@@ -66,7 +66,7 @@ if (!function_exists("getConsoleDB")) {
|
||||
}
|
||||
|
||||
// Allows overriding
|
||||
if (!function_exists("getProjectDB")) {
|
||||
if (!function_exists('getProjectDB')) {
|
||||
function getProjectDB(Document $project): Database
|
||||
{
|
||||
global $register;
|
||||
@@ -113,7 +113,7 @@ if (!function_exists("getProjectDB")) {
|
||||
}
|
||||
|
||||
// Allows overriding
|
||||
if (!function_exists("getCache")) {
|
||||
if (!function_exists('getCache')) {
|
||||
function getCache(): Cache
|
||||
{
|
||||
global $register;
|
||||
@@ -135,7 +135,14 @@ if (!function_exists("getCache")) {
|
||||
}
|
||||
}
|
||||
|
||||
$realtime = new Realtime();
|
||||
if (!function_exists('getRealtime')) {
|
||||
function getRealtime(): Realtime
|
||||
{
|
||||
return new Realtime();
|
||||
}
|
||||
}
|
||||
|
||||
$realtime = getRealtime();
|
||||
|
||||
/**
|
||||
* Table for statistics across all workers.
|
||||
@@ -184,8 +191,12 @@ $logError = function (Throwable $error, string $action) use ($register) {
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Realtime log pushed with status code: ' . $responseCode);
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
@@ -463,7 +474,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
*
|
||||
* Abuse limits are connecting 128 times per minute and ip address.
|
||||
*/
|
||||
$timeLimit = new AbuseDatabase('url:{url},ip:{ip}', 128, 60, $dbForProject);
|
||||
$timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject);
|
||||
$timeLimit
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getURI());
|
||||
@@ -563,7 +574,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
*
|
||||
* Abuse limits are sending 32 times per minute and connection.
|
||||
*/
|
||||
$timeLimit = new AbuseDatabase('url:{url},connection:{connection}', 32, 60, $database);
|
||||
$timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database);
|
||||
|
||||
$timeLimit
|
||||
->setParam('{connection}', $connection)
|
||||
|
||||
@@ -166,7 +166,7 @@ $image = $this->getParam('image', '');
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: <?php echo $organization; ?>/console:appwrite/console:5.0.0-rc16
|
||||
image: <?php echo $organization; ?>/console:5.0.12
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -336,6 +336,9 @@ $image = $this->getParam('image', '');
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
|
||||
appwrite-worker-databases:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
@@ -528,6 +531,8 @@ $image = $this->getParam('image', '');
|
||||
- _APP_SMTP_USERNAME
|
||||
- _APP_SMTP_PASSWORD
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DOMAIN
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
|
||||
appwrite-worker-messaging:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
@@ -676,6 +681,7 @@ $image = $this->getParam('image', '');
|
||||
entrypoint: worker-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-usage-dump
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
depends_on:
|
||||
@@ -789,7 +795,7 @@ $image = $this->getParam('image', '');
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.6.7
|
||||
image: openruntimes/executor:0.6.11
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
||||
+11
-2
@@ -272,6 +272,11 @@ Server::setResource('deviceForCache', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
Server::setResource(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
|
||||
);
|
||||
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
@@ -346,8 +351,12 @@ $worker
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Usage stats log pushed with status code: ' . $responseCode);
|
||||
try {
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Error log pushed with status code: ' . $responseCode);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
|
||||
+7
-7
@@ -43,24 +43,24 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-sockets": "*",
|
||||
"appwrite/php-runtimes": "0.15.*",
|
||||
"appwrite/php-runtimes": "0.16.*",
|
||||
"appwrite/php-clamav": "2.0.*",
|
||||
"utopia-php/abuse": "0.40.*",
|
||||
"utopia-php/abuse": "0.43.0",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "0.41.*",
|
||||
"utopia-php/audit": "0.43.0",
|
||||
"utopia-php/cache": "0.10.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.51.*",
|
||||
"utopia-php/database": "0.53.8",
|
||||
"utopia-php/domains": "0.5.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.2.*",
|
||||
"utopia-php/image": "0.6.*",
|
||||
"utopia-php/image": "0.7.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.12.*",
|
||||
"utopia-php/migration": "0.5.*",
|
||||
"utopia-php/migration": "0.6.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.5.*",
|
||||
@@ -69,7 +69,7 @@
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.8.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/vcs": "0.8.*",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
|
||||
Generated
+306
-213
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -103,6 +103,7 @@ services:
|
||||
- _APP_CONSOLE_HOSTNAMES
|
||||
- _APP_SYSTEM_EMAIL_NAME
|
||||
- _APP_SYSTEM_EMAIL_ADDRESS
|
||||
- _APP_SYSTEM_TEAM_EMAIL
|
||||
- _APP_EMAIL_SECURITY
|
||||
- _APP_SYSTEM_RESPONSE_FORMAT
|
||||
- _APP_OPTIONS_ABUSE
|
||||
@@ -195,7 +196,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.0.0-rc18
|
||||
image: appwrite/console:5.0.12
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -862,7 +863,7 @@ services:
|
||||
|
||||
appwrite-assistant:
|
||||
container_name: appwrite-assistant
|
||||
image: appwrite/assistant:0.4.0
|
||||
image: appwrite/assistant:0.5.0
|
||||
networks:
|
||||
- appwrite
|
||||
environment:
|
||||
@@ -873,7 +874,7 @@ services:
|
||||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.6.7
|
||||
image: openruntimes/executor:0.6.11
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
@@ -11,7 +11,8 @@ const result = await databases.updateBooleanAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
false // default
|
||||
false, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -11,7 +11,8 @@ const result = await databases.updateDatetimeAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'' // default
|
||||
'', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -11,7 +11,8 @@ const result = await databases.updateEmailAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'email@example.com' // default
|
||||
'email@example.com', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -12,7 +12,8 @@ const result = await databases.updateEnumAttribute(
|
||||
'', // key
|
||||
[], // elements
|
||||
false, // required
|
||||
'<DEFAULT>' // default
|
||||
'<DEFAULT>', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -13,7 +13,8 @@ const result = await databases.updateFloatAttribute(
|
||||
false, // required
|
||||
null, // min
|
||||
null, // max
|
||||
null // default
|
||||
null, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -13,7 +13,8 @@ const result = await databases.updateIntegerAttribute(
|
||||
false, // required
|
||||
null, // min
|
||||
null, // max
|
||||
null // default
|
||||
null, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -11,7 +11,8 @@ const result = await databases.updateIpAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'' // default
|
||||
'', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -10,7 +10,8 @@ const result = await databases.updateRelationshipAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
RelationMutate.Cascade // onDelete (optional)
|
||||
RelationMutate.Cascade, // onDelete (optional)
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -11,7 +11,9 @@ const result = await databases.updateStringAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'<DEFAULT>' // default
|
||||
'<DEFAULT>', // default
|
||||
null, // size (optional)
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -11,7 +11,8 @@ const result = await databases.updateUrlAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'https://example.com' // default
|
||||
'https://example.com', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -13,4 +13,5 @@ AttributeBoolean result = await databases.updateBooleanAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: false,
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -13,4 +13,5 @@ AttributeDatetime result = await databases.updateDatetimeAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: '',
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -13,4 +13,5 @@ AttributeEmail result = await databases.updateEmailAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: 'email@example.com',
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -14,4 +14,5 @@ AttributeEnum result = await databases.updateEnumAttribute(
|
||||
elements: [],
|
||||
xrequired: false,
|
||||
xdefault: '<DEFAULT>',
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -15,4 +15,5 @@ AttributeFloat result = await databases.updateFloatAttribute(
|
||||
min: 0,
|
||||
max: 0,
|
||||
xdefault: 0,
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -15,4 +15,5 @@ AttributeInteger result = await databases.updateIntegerAttribute(
|
||||
min: 0,
|
||||
max: 0,
|
||||
xdefault: 0,
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -13,4 +13,5 @@ AttributeIp result = await databases.updateIpAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: '',
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -12,4 +12,5 @@ AttributeRelationship result = await databases.updateRelationshipAttribute(
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
key: '',
|
||||
onDelete: RelationMutate.cascade, // (optional)
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -13,4 +13,6 @@ AttributeString result = await databases.updateStringAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: '<DEFAULT>',
|
||||
size: 0, // (optional)
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -13,4 +13,5 @@ AttributeUrl result = await databases.updateUrlAttribute(
|
||||
key: '',
|
||||
xrequired: false,
|
||||
xdefault: 'https://example.com',
|
||||
newKey: '', // (optional)
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
Client client = Client()
|
||||
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
Functions functions = Functions(client);
|
||||
|
||||
|
||||
@@ -12,5 +12,6 @@ const response = await databases.updateBooleanAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
false // default
|
||||
false, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -12,5 +12,6 @@ const response = await databases.updateDatetimeAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'' // default
|
||||
'', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -12,5 +12,6 @@ const response = await databases.updateEmailAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'email@example.com' // default
|
||||
'email@example.com', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -13,5 +13,6 @@ const response = await databases.updateEnumAttribute(
|
||||
'', // key
|
||||
[], // elements
|
||||
false, // required
|
||||
'<DEFAULT>' // default
|
||||
'<DEFAULT>', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -14,5 +14,6 @@ const response = await databases.updateFloatAttribute(
|
||||
false, // required
|
||||
null, // min
|
||||
null, // max
|
||||
null // default
|
||||
null, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -14,5 +14,6 @@ const response = await databases.updateIntegerAttribute(
|
||||
false, // required
|
||||
null, // min
|
||||
null, // max
|
||||
null // default
|
||||
null, // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -12,5 +12,6 @@ const response = await databases.updateIpAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'' // default
|
||||
'', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -11,5 +11,6 @@ const response = await databases.updateRelationshipAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
RelationMutate.Cascade // onDelete (optional)
|
||||
RelationMutate.Cascade, // onDelete (optional)
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -12,5 +12,7 @@ const response = await databases.updateStringAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'<DEFAULT>' // default
|
||||
'<DEFAULT>', // default
|
||||
null, // size (optional)
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -12,5 +12,6 @@ const response = await databases.updateUrlAttribute(
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'', // key
|
||||
false, // required
|
||||
'https://example.com' // default
|
||||
'https://example.com', // default
|
||||
'' // newKey (optional)
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Client, Functions } from "https://deno.land/x/appwrite/mod.ts";
|
||||
const client = new Client()
|
||||
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const functions = new Functions(client);
|
||||
|
||||
|
||||
@@ -14,5 +14,6 @@ AttributeBoolean result = await databases.UpdateBooleanAttribute(
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
key: "",
|
||||
required: false,
|
||||
default: false
|
||||
default: false,
|
||||
newKey: "" // optional
|
||||
);
|
||||
@@ -14,5 +14,6 @@ AttributeDatetime result = await databases.UpdateDatetimeAttribute(
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
key: "",
|
||||
required: false,
|
||||
default: ""
|
||||
default: "",
|
||||
newKey: "" // optional
|
||||
);
|
||||
@@ -14,5 +14,6 @@ AttributeEmail result = await databases.UpdateEmailAttribute(
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
key: "",
|
||||
required: false,
|
||||
default: "email@example.com"
|
||||
default: "email@example.com",
|
||||
newKey: "" // optional
|
||||
);
|
||||
@@ -15,5 +15,6 @@ AttributeEnum result = await databases.UpdateEnumAttribute(
|
||||
key: "",
|
||||
elements: new List<string>(),
|
||||
required: false,
|
||||
default: "<DEFAULT>"
|
||||
default: "<DEFAULT>",
|
||||
newKey: "" // optional
|
||||
);
|
||||
@@ -16,5 +16,6 @@ AttributeFloat result = await databases.UpdateFloatAttribute(
|
||||
required: false,
|
||||
min: 0,
|
||||
max: 0,
|
||||
default: 0
|
||||
default: 0,
|
||||
newKey: "" // optional
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user