mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
3651
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3607471eb | ||
|
|
d1f7bd26c8 | ||
|
|
7296027e43 | ||
|
|
1ce00b139d | ||
|
|
2ebd500d6b | ||
|
|
6b5b3ec865 | ||
|
|
5fa1da3610 | ||
|
|
e5f841e353 | ||
|
|
3a38b492f5 | ||
|
|
5f4f6cd9c5 | ||
|
|
f2a337d86e | ||
|
|
71b4c483c2 | ||
|
|
d9569df1cc | ||
|
|
d1efcd6a61 | ||
|
|
cea7ab8d4c | ||
|
|
cfc178b046 | ||
|
|
50aba6a1c2 | ||
|
|
5fd5c875fc | ||
|
|
1ceaa599b5 | ||
|
|
1138b4fd59 | ||
|
|
fae5c5b92e | ||
|
|
9a446ec0ec | ||
|
|
675ad1d0c6 | ||
|
|
a2463e114e | ||
|
|
9fab5c46f1 | ||
|
|
d730b41b5f | ||
|
|
66228a2462 | ||
|
|
fddac97835 | ||
|
|
e4f653be13 | ||
|
|
53df65a3c4 | ||
|
|
2f8b526c01 | ||
|
|
c1cf6acc7a | ||
|
|
dd25899f74 | ||
|
|
fc2f1cef31 | ||
|
|
b2c7a3eb33 | ||
|
|
55a6732444 | ||
|
|
970c35b31a | ||
|
|
38eb24d0ad | ||
|
|
186b92d5af | ||
|
|
7ad08bfef2 | ||
|
|
6f87757c53 | ||
|
|
4f0dd56afc | ||
|
|
7918ee2f20 | ||
|
|
748abb29cf | ||
|
|
c6be81592f | ||
|
|
e9f8a33515 | ||
|
|
48bf74b881 | ||
|
|
702c92fb5d | ||
|
|
28103c9fd9 | ||
|
|
38cf901579 | ||
|
|
b56603958a | ||
|
|
4d7611f569 | ||
|
|
9ff6faa2d2 | ||
|
|
5e836691e2 | ||
|
|
0a92e3dc55 | ||
|
|
9179a70992 | ||
|
|
72e9ddd3f1 | ||
|
|
b0a70d22ab | ||
|
|
87aa7ea2f8 | ||
|
|
4c68e396c1 | ||
|
|
1980b8c677 | ||
|
|
77818e52fa | ||
|
|
18f5418f5c | ||
|
|
ede673cc7e | ||
|
|
2d2a6b6721 | ||
|
|
9fbb8c2e3a | ||
|
|
e633389707 | ||
|
|
97581d5836 | ||
|
|
6853a44f03 | ||
|
|
428e9e73fe | ||
|
|
ce596b2f2b | ||
|
|
f5e9094bdc | ||
|
|
7eaa5f1e35 | ||
|
|
57411db2d3 | ||
|
|
c25e014be1 | ||
|
|
25e2771fa7 | ||
|
|
579a95aecc | ||
|
|
3286279d3c | ||
|
|
f2705c30a3 | ||
|
|
f0c704c3f1 | ||
|
|
7196114339 | ||
|
|
56a337eac4 | ||
|
|
2432210bbb | ||
|
|
e225bbe3c9 | ||
|
|
ce1ad823b5 | ||
|
|
6093262f21 | ||
|
|
9d75e65ba9 | ||
|
|
a4e055e21b | ||
|
|
7ec4b2171c | ||
|
|
8645757f28 | ||
|
|
41d207e862 | ||
|
|
df96a5de90 | ||
|
|
eccde7b1f9 | ||
|
|
56aa30fd52 | ||
|
|
651c826de1 | ||
|
|
deea23c17e | ||
|
|
d0f234e726 | ||
|
|
7a4898fadd | ||
|
|
d2b11f91fa | ||
|
|
845fd7ba68 | ||
|
|
ba47e7ddc0 | ||
|
|
cb303ca71b | ||
|
|
a2534e643e | ||
|
|
e0a58f369c | ||
|
|
06671709d6 | ||
|
|
d4ec392a6f | ||
|
|
00453dd9c2 | ||
|
|
8fdb9e6592 | ||
|
|
5c6cbbe370 | ||
|
|
9f02ed700d | ||
|
|
afdad2e0ef | ||
|
|
ababe6a382 | ||
|
|
7798e28129 | ||
|
|
68f3f147ab | ||
|
|
73d733c70a | ||
|
|
6f9f9ea0e4 | ||
|
|
c74bdfa2a6 | ||
|
|
e87e2fc16d | ||
|
|
73ac9f796d | ||
|
|
d7733dde01 | ||
|
|
931b6fa385 | ||
|
|
6a9011af72 | ||
|
|
5c9ad6fddd | ||
|
|
f5c4e4cd8a | ||
|
|
9a071df378 | ||
|
|
b68f601240 | ||
|
|
36f1791a92 | ||
|
|
fac4b6f600 | ||
|
|
23e6c1adf6 | ||
|
|
cd8485fae4 | ||
|
|
55ba25ad83 | ||
|
|
8bc547e825 | ||
|
|
071b2e5826 | ||
|
|
ea8804fd52 | ||
|
|
2ee7a3a356 | ||
|
|
02cd8720f7 | ||
|
|
0765aac5d2 | ||
|
|
072232e1af | ||
|
|
0fd7db3c36 | ||
|
|
876b0da2e2 | ||
|
|
5e2e28995d | ||
|
|
9ac4a2bfd5 | ||
|
|
8ce6d38922 | ||
|
|
3b6128c560 | ||
|
|
da75a2e409 | ||
|
|
1225c8b7c3 | ||
|
|
37f690c3bd | ||
|
|
dd2bda0d98 | ||
|
|
95f299779d | ||
|
|
b75d3ff07c | ||
|
|
edce3cceed | ||
|
|
fad53d3e89 | ||
|
|
91c5e066ec | ||
|
|
4ec8f69da6 | ||
|
|
075ed65a2f | ||
|
|
9ff96a79d1 | ||
|
|
54ad50bd4b | ||
|
|
7d19fe87f5 | ||
|
|
c5c115d697 | ||
|
|
3614cb384a | ||
|
|
ef35526a69 | ||
|
|
ab83dfedac | ||
|
|
d7b32cfc3e | ||
|
|
77971a4eb5 | ||
|
|
0244154017 | ||
|
|
79ce648211 | ||
|
|
5960d28428 | ||
|
|
b81e9ffd47 | ||
|
|
1762c4f491 | ||
|
|
adca405d74 | ||
|
|
99b56b3584 | ||
|
|
31e6492dbc | ||
|
|
3545e0740f | ||
|
|
9d13d72c71 | ||
|
|
f774721701 | ||
|
|
5163ce7cc9 | ||
|
|
e79cb913ed | ||
|
|
48ca2530e6 | ||
|
|
0839822a66 | ||
|
|
9a712686f2 | ||
|
|
8be36088d7 | ||
|
|
f0df316b4f | ||
|
|
0368cdb669 | ||
|
|
f1017e6fe9 | ||
|
|
777df4efd1 | ||
|
|
a1eb1c1166 | ||
|
|
4443c20865 | ||
|
|
c97bdf058e | ||
|
|
56efd5e11a | ||
|
|
83697784d7 | ||
|
|
0d12b13241 | ||
|
|
12afc294bd | ||
|
|
bdd79c7b10 | ||
|
|
76f2e0eeac | ||
|
|
94f2853a5f | ||
|
|
56172ac196 | ||
|
|
b9efa52a92 | ||
|
|
ecbc9aecb3 | ||
|
|
880dbfd128 | ||
|
|
9bd1d864e9 | ||
|
|
731b49db95 | ||
|
|
6d97ca7569 | ||
|
|
fa3899a713 | ||
|
|
1d35eef756 | ||
|
|
901ee1fe16 | ||
|
|
3b2e968703 | ||
|
|
189578a248 | ||
|
|
2db48a1b13 | ||
|
|
956087473e | ||
|
|
b3b89c10f3 | ||
|
|
6885e6ea0e | ||
|
|
73668a7283 | ||
|
|
2b08ddee32 | ||
|
|
257d623fab | ||
|
|
c9a39fce4f | ||
|
|
9ed7da3b38 | ||
|
|
f223f10de4 | ||
|
|
287441a913 | ||
|
|
70b5281a0b | ||
|
|
e84372aae2 | ||
|
|
df7a02a031 | ||
|
|
352b32b54e | ||
|
|
1409ea4dbf | ||
|
|
07cc9f8a26 | ||
|
|
de460f1810 | ||
|
|
b53d0e9c21 | ||
|
|
bf5187d74d | ||
|
|
4ca1b9c986 | ||
|
|
d71cf65300 | ||
|
|
e912df3368 | ||
|
|
03612a5741 | ||
|
|
3fd5142f22 | ||
|
|
a113214f01 | ||
|
|
96b1cc60b5 | ||
|
|
5d173b8e0d | ||
|
|
ae474a1f7c | ||
|
|
9b4d9d7c6d | ||
|
|
a185d6c7f1 | ||
|
|
7db825f621 | ||
|
|
2244ba4f98 | ||
|
|
985ca3843b | ||
|
|
d022ee6622 | ||
|
|
8c1d89c33c | ||
|
|
6c6154b3d4 | ||
|
|
db472b19c7 | ||
|
|
837cebcdda | ||
|
|
a7f81bd628 | ||
|
|
2ed495fe3b | ||
|
|
a8cd99873f | ||
|
|
93946a074d | ||
|
|
89b85de03e | ||
|
|
2e421f15ab | ||
|
|
f56a8a40df | ||
|
|
563f2e22f9 | ||
|
|
d87e198913 | ||
|
|
5782f7b8c6 | ||
|
|
a3e7fa29f4 | ||
|
|
e190e1aebe | ||
|
|
7871e9aa4c | ||
|
|
fcf3283365 | ||
|
|
55e53abcfc | ||
|
|
f6ae33aeba | ||
|
|
ffa683e932 | ||
|
|
65c6123ccd | ||
|
|
bafe948578 | ||
|
|
bd620cc924 | ||
|
|
567cb01b99 | ||
|
|
b558a284f3 | ||
|
|
cf39092c98 | ||
|
|
03dc7e2a04 | ||
|
|
9723792a29 | ||
|
|
07e3a29b63 | ||
|
|
6c15326f8f | ||
|
|
c1b0dc73f8 | ||
|
|
deab7caed9 | ||
|
|
610861cd93 | ||
|
|
f951da2f55 | ||
|
|
1de9d9ca26 | ||
|
|
39388e9b92 | ||
|
|
d01ddb4c86 | ||
|
|
7e06115686 | ||
|
|
c3ff315668 | ||
|
|
e8b11b2d14 | ||
|
|
a164991c75 | ||
|
|
5fe65ab991 | ||
|
|
42ebaf6541 | ||
|
|
007d47e586 | ||
|
|
ac08640bc4 | ||
|
|
46f07faa98 | ||
|
|
a492ad55d5 | ||
|
|
253dd5e58c | ||
|
|
334e3cc57d | ||
|
|
10b7503d62 | ||
|
|
ad2c068e75 | ||
|
|
a1fca6ea2d | ||
|
|
66daa80c07 | ||
|
|
2dda3a02f2 | ||
|
|
4715bbcf57 | ||
|
|
9988347235 | ||
|
|
b0d4d9183d | ||
|
|
72d48eddee | ||
|
|
5c1adf1f68 | ||
|
|
ef20d5f007 | ||
|
|
4672b31685 | ||
|
|
38d7401aa9 | ||
|
|
28e6de81d4 | ||
|
|
45a2268910 | ||
|
|
b1f624bc0d | ||
|
|
7261febebe | ||
|
|
fd389bc02f | ||
|
|
304cb16553 | ||
|
|
b1333b39c8 | ||
|
|
cdf9745a2a | ||
|
|
73370ca746 | ||
|
|
e654ec2720 | ||
|
|
a15d03f19e | ||
|
|
ac2e7efb3c | ||
|
|
f6bef128e4 | ||
|
|
a3cf72bc15 | ||
|
|
c97e7ce328 | ||
|
|
47f9123b78 | ||
|
|
45b68fa44f | ||
|
|
5074a139df | ||
|
|
222421d8f3 | ||
|
|
4142ffdb70 | ||
|
|
30690f6bc4 | ||
|
|
543246c50b | ||
|
|
837bad59a4 | ||
|
|
a2dfd692f1 | ||
|
|
71150ced1a | ||
|
|
d7104d9cd6 | ||
|
|
2bc85e4708 | ||
|
|
9f9daf900a | ||
|
|
e3a2135515 | ||
|
|
59adaff541 | ||
|
|
3f992b316e | ||
|
|
b751af6db9 | ||
|
|
78a057ebf9 | ||
|
|
7ea8d761e4 | ||
|
|
ff9bccf1ff | ||
|
|
708994d4a6 | ||
|
|
e3737af3fe | ||
|
|
b014eda788 | ||
|
|
ff73939855 | ||
|
|
fdd9a1caf3 | ||
|
|
1a33ffa867 | ||
|
|
8f363eae7a | ||
|
|
ab68bb4ea1 | ||
|
|
c5da386d67 | ||
|
|
90e93f3255 | ||
|
|
e22ad54e39 | ||
|
|
4ee8fd5629 | ||
|
|
072d896bb9 | ||
|
|
54c6f9f4e1 | ||
|
|
3af214172c | ||
|
|
3d07600a48 | ||
|
|
11b3abebd1 | ||
|
|
41a6a6dbf7 | ||
|
|
6279784009 | ||
|
|
746390e30a | ||
|
|
d702f3581f | ||
|
|
de0de5f225 | ||
|
|
2f53260197 | ||
|
|
d79eea0f4b | ||
|
|
b513c2fbad | ||
|
|
fe27670e1f | ||
|
|
ec2da723fb | ||
|
|
46593778c9 | ||
|
|
674c23dfde | ||
|
|
69d5a25c62 | ||
|
|
0a177713f0 | ||
|
|
ea58cb4aff | ||
|
|
4dfaa5e69a | ||
|
|
8dfdef6006 | ||
|
|
5dbed8329e | ||
|
|
a983630e0e | ||
|
|
f44a642998 | ||
|
|
cefd5660b9 | ||
|
|
9d0cbd3b68 | ||
|
|
6070e1de54 | ||
|
|
de38fed306 | ||
|
|
30eda18ac1 | ||
|
|
25f3d4a59b | ||
|
|
8b71e318c2 | ||
|
|
0cb2ed14f4 | ||
|
|
5e872f2fe5 | ||
|
|
efe093ce3c | ||
|
|
bb01a6d817 | ||
|
|
f239296059 | ||
|
|
b44a100439 | ||
|
|
6d3adeb4ed | ||
|
|
01556184c7 | ||
|
|
dcbdbfa053 | ||
|
|
4d4ed16839 | ||
|
|
b819381bed | ||
|
|
20c538ec83 | ||
|
|
991d8749b2 | ||
|
|
5f0f03c170 | ||
|
|
2e9087285d | ||
|
|
b382031ae7 | ||
|
|
8406551cef | ||
|
|
0d0de17d15 | ||
|
|
e38b5cee8e | ||
|
|
97bbe1a465 | ||
|
|
6db93b6dfb | ||
|
|
089a1b295c | ||
|
|
948b14d139 | ||
|
|
1a00e3829a | ||
|
|
1bdb58e3cf | ||
|
|
d7cdaa5565 | ||
|
|
2a7fae54e0 | ||
|
|
1e1ab2a8ae | ||
|
|
bd0139ef3b | ||
|
|
5f80f87ba9 | ||
|
|
56d8c176dd | ||
|
|
ad1587872b | ||
|
|
dd5a424022 | ||
|
|
2fcaa02226 | ||
|
|
5730c3deb6 | ||
|
|
1f7a4b86d1 | ||
|
|
a231a9853f | ||
|
|
64ba896738 | ||
|
|
4813e6b4d5 | ||
|
|
315eaaa562 | ||
|
|
fc752039a4 | ||
|
|
e7a51fac74 | ||
|
|
d2b0218aba | ||
|
|
c99ed47e63 | ||
|
|
35f16118b1 | ||
|
|
92a59cdf3f | ||
|
|
95ac8721f0 | ||
|
|
c7f2785b45 | ||
|
|
2ef43e3249 | ||
|
|
85de5e9106 | ||
|
|
210b4558c6 | ||
|
|
a47618e01a | ||
|
|
dd8a64b3a3 | ||
|
|
8ea6647b22 | ||
|
|
259f868cc8 | ||
|
|
ced481f78c | ||
|
|
f055adc05f | ||
|
|
de8552e901 | ||
|
|
7f95ff31bd | ||
|
|
12b2deda11 | ||
|
|
e20b2f3340 | ||
|
|
eda112755a | ||
|
|
dd99ab7b00 | ||
|
|
202e4c01d8 | ||
|
|
14ea8b2197 | ||
|
|
e7fc427291 | ||
|
|
d2362aeab0 | ||
|
|
831960245f | ||
|
|
8feb3dc43c | ||
|
|
0ee49e7895 | ||
|
|
a80da7526d | ||
|
|
4a61718cff | ||
|
|
7694e19322 | ||
|
|
9698dc2115 | ||
|
|
948936dcff | ||
|
|
91de1cbcf3 | ||
|
|
c284c417bb | ||
|
|
a13c671a2b | ||
|
|
421cebced8 | ||
|
|
9163aa360e | ||
|
|
5ec548cc06 | ||
|
|
1057ab4e42 | ||
|
|
b39aa060c8 | ||
|
|
eba34a294d | ||
|
|
792286c955 | ||
|
|
0075956c6c | ||
|
|
715273798e | ||
|
|
8c7fa98fe2 | ||
|
|
7dc5617339 | ||
|
|
35740fe65b | ||
|
|
2f5e7e1e8c | ||
|
|
fc0b8730a9 | ||
|
|
26ea9d675a | ||
|
|
d0c253e45d | ||
|
|
418a432d6c | ||
|
|
3353bad8ff | ||
|
|
57dd721a61 | ||
|
|
b30571dcd0 | ||
|
|
917febcf49 | ||
|
|
373e230ab0 | ||
|
|
71bb70dec4 | ||
|
|
18cd83c2af | ||
|
|
8f6a151980 | ||
|
|
6c43b44441 | ||
|
|
c4a5c73717 | ||
|
|
e0c363e8e5 | ||
|
|
673b0bb083 | ||
|
|
080abbbb79 | ||
|
|
dd34dfce22 | ||
|
|
bba9284c3e | ||
|
|
d1eae37577 | ||
|
|
727c3839d6 | ||
|
|
36cc6d8621 | ||
|
|
ac73be78b5 | ||
|
|
878021b9a3 | ||
|
|
a252c7e469 | ||
|
|
8d3903cd37 | ||
|
|
e7189efcff | ||
|
|
53adb4e114 | ||
|
|
6567b23c85 | ||
|
|
30f088ad35 | ||
|
|
00ec95fc9f | ||
|
|
eb5e853f2d | ||
|
|
6acb07109a | ||
|
|
b989a35b62 | ||
|
|
99c6dfb4ae | ||
|
|
4f31fb5dc8 | ||
|
|
4271e34b39 | ||
|
|
ad5bd3b328 | ||
|
|
999dfe5d63 | ||
|
|
28087d708d | ||
|
|
836ab62285 | ||
|
|
d830f5c262 | ||
|
|
8b431a8e44 | ||
|
|
261830c122 | ||
|
|
d75831380b | ||
|
|
3d01bf6505 | ||
|
|
f63aeb50a1 | ||
|
|
3439978253 | ||
|
|
baddbf7f03 | ||
|
|
9f92214c9d | ||
|
|
96e67f968f | ||
|
|
74eaef4e9d | ||
|
|
a58dbe7c66 | ||
|
|
db79f92b39 | ||
|
|
5dd31bcdfb | ||
|
|
48f143547a | ||
|
|
2220222f48 | ||
|
|
ebfc114980 | ||
|
|
2a9345cbcd | ||
|
|
f324576686 | ||
|
|
762d52bca8 | ||
|
|
2ede405784 | ||
|
|
a309beddf5 | ||
|
|
4a495f61d5 | ||
|
|
683447b3b5 | ||
|
|
657f2bc334 | ||
|
|
a34223748b | ||
|
|
ae9a518250 | ||
|
|
a233a30d28 | ||
|
|
44c85da574 | ||
|
|
153f2424d1 | ||
|
|
bb0e4fe049 | ||
|
|
61d3d6755e | ||
|
|
8173aec33a | ||
|
|
48e8e72431 | ||
|
|
a6fcc59ead | ||
|
|
a976390e5f | ||
|
|
29db442b99 | ||
|
|
0250754e6f | ||
|
|
8edb592138 | ||
|
|
018750161e | ||
|
|
6a79bea5df | ||
|
|
bc6731ff6b | ||
|
|
99823656f0 | ||
|
|
7d8162309c | ||
|
|
dd7bd108cc | ||
|
|
a027e6eceb | ||
|
|
0c759e958d | ||
|
|
5f8d3ff6ee | ||
|
|
e37f70246e | ||
|
|
284150a84a | ||
|
|
8b000aff40 | ||
|
|
3b6174b5a7 | ||
|
|
b1372a39b0 | ||
|
|
c67852a7fb | ||
|
|
3e325afb61 | ||
|
|
fdbb5b9810 | ||
|
|
43adf758bd | ||
|
|
83639ea6f8 | ||
|
|
17a36dbce3 | ||
|
|
956eab5ab5 | ||
|
|
08a986e433 | ||
|
|
05318b3168 | ||
|
|
390f407eb5 | ||
|
|
cf4be51094 | ||
|
|
9e4a65605c | ||
|
|
1498d0f628 | ||
|
|
44875e2d18 | ||
|
|
fbebf7deac | ||
|
|
c585dfda16 | ||
|
|
5ca7b5ffe8 | ||
|
|
8411e81e28 | ||
|
|
68cc1b5db4 | ||
|
|
410533be27 | ||
|
|
f85b8a5bdc | ||
|
|
8faa46de9b | ||
|
|
d3b8d49a3e | ||
|
|
cd4b7c1c36 | ||
|
|
0d61a7fcf5 | ||
|
|
aae2cea73a | ||
|
|
ac70537ece | ||
|
|
6b88f66d14 | ||
|
|
4e3bcd6cd0 | ||
|
|
fbb9bb8941 | ||
|
|
3ffd59f18c | ||
|
|
da4e7536ca | ||
|
|
3cb97a62c8 | ||
|
|
b80e82e9c1 | ||
|
|
c4bbf08d52 | ||
|
|
87bc593570 | ||
|
|
b71ddd1d26 | ||
|
|
750dcc2a56 | ||
|
|
c46ba06c51 | ||
|
|
dbb05c46ba | ||
|
|
e976bd3cb1 | ||
|
|
8b0a78bb36 | ||
|
|
5abe9ad73c | ||
|
|
586fd46bcd | ||
|
|
3bd3b8d8e9 | ||
|
|
4a92db4dc3 | ||
|
|
9c95e40458 | ||
|
|
860f077787 | ||
|
|
40c71a7b53 | ||
|
|
9a09ee23f1 | ||
|
|
deefff537d | ||
|
|
6bf370a058 | ||
|
|
44a82de09b | ||
|
|
3b6035b585 | ||
|
|
912636b54c | ||
|
|
27ddf8630b | ||
|
|
280a44e1cd | ||
|
|
855c3d3b84 | ||
|
|
eb47894970 | ||
|
|
797c3f2443 | ||
|
|
11915e1667 | ||
|
|
0d79f19dcd | ||
|
|
3682c1d05b | ||
|
|
8bcf349c38 | ||
|
|
50dc4c3790 | ||
|
|
841c5479ed | ||
|
|
68e1a0b114 | ||
|
|
1b533daeaa | ||
|
|
5db924627a | ||
|
|
29edecf05d | ||
|
|
ee0950449a | ||
|
|
18618b8e8b | ||
|
|
006cb4665e | ||
|
|
aff29ed197 | ||
|
|
ef1e5a9674 | ||
|
|
344669db46 | ||
|
|
10695a021a | ||
|
|
62618445e9 | ||
|
|
390fbc52eb | ||
|
|
35b7a2ee96 | ||
|
|
6be62fb20e | ||
|
|
e04295918f | ||
|
|
c07953a640 | ||
|
|
b6e4ec822b | ||
|
|
bfe8b98008 | ||
|
|
85ed70f346 | ||
|
|
451c6334bb | ||
|
|
bb3c99c58b | ||
|
|
ea5bd5160a | ||
|
|
522ae8d538 | ||
|
|
4a126efd3f | ||
|
|
727338cb9a | ||
|
|
7288520f38 | ||
|
|
174f57cfb1 | ||
|
|
98fbe18ac4 | ||
|
|
9539e27907 | ||
|
|
1084631d0f | ||
|
|
c13589c1ea | ||
|
|
b031e13997 | ||
|
|
f5a4701944 | ||
|
|
22effdd88a | ||
|
|
f0052cbd8e | ||
|
|
30e39f0bcf | ||
|
|
a51288da57 | ||
|
|
64b725a6f7 | ||
|
|
ff06e4b238 | ||
|
|
e1ecf9aad7 | ||
|
|
8dd5eb09f0 | ||
|
|
3e4a10f42e | ||
|
|
dc6c8d9b9a | ||
|
|
5b32f6d9a9 | ||
|
|
6e0c54a151 | ||
|
|
318ee64b93 | ||
|
|
a59336ce95 | ||
|
|
702657ebb4 | ||
|
|
b8b78adfdc | ||
|
|
e750c19660 | ||
|
|
cca33c6c5d | ||
|
|
c4be0260c3 | ||
|
|
af391b8f7a | ||
|
|
ada54cacd0 | ||
|
|
7de2f69f38 | ||
|
|
1c24421a95 | ||
|
|
447f3f6756 | ||
|
|
c621b66195 | ||
|
|
aea13bad72 | ||
|
|
825738ced9 | ||
|
|
a376bdcb60 | ||
|
|
9412013360 | ||
|
|
3ae1d9b8e6 | ||
|
|
d7d41b4b6b | ||
|
|
3ef9a3397f | ||
|
|
3c71164741 | ||
|
|
c784cab798 | ||
|
|
a14ab321bf | ||
|
|
310ff90d22 | ||
|
|
465dad522e | ||
|
|
de8122c02f | ||
|
|
6260a1a156 | ||
|
|
60a63d9eb9 | ||
|
|
c92cbbead2 | ||
|
|
fc272daeb2 | ||
|
|
2c255a7394 | ||
|
|
920cff8ce7 | ||
|
|
f469d1ba45 | ||
|
|
0570e26a48 | ||
|
|
5fbf961a9a | ||
|
|
d15af1ae17 | ||
|
|
bbcce5e018 | ||
|
|
cb9bfb2b54 | ||
|
|
ae0b9e78aa | ||
|
|
c2e8fc5733 | ||
|
|
43d4d6fec3 | ||
|
|
c76057fd37 | ||
|
|
36d4719a3b | ||
|
|
ab7eb01438 | ||
|
|
9626d86b22 | ||
|
|
0b883df5ec | ||
|
|
83e19a37ee | ||
|
|
cdb4c60c3c | ||
|
|
66b805829c | ||
|
|
5e7de81966 | ||
|
|
f6b7e0392b | ||
|
|
b4bf0cf8d0 | ||
|
|
f5fe47535d | ||
|
|
6d1ae05b64 | ||
|
|
3a6ed84ce7 | ||
|
|
702bb413dc | ||
|
|
250ea93d3f | ||
|
|
2dea0ec3b8 | ||
|
|
f5e66a96e7 | ||
|
|
22974cea02 | ||
|
|
134dba0b43 | ||
|
|
23258c6e43 | ||
|
|
f468248f45 | ||
|
|
72a37516d3 | ||
|
|
180262d166 | ||
|
|
7723762dd1 | ||
|
|
9e3b9b8e59 | ||
|
|
3dc619f57d | ||
|
|
745ae7a4c8 | ||
|
|
aef565c8ff | ||
|
|
df791068d6 | ||
|
|
902bcfc3be | ||
|
|
5d48d2ace0 | ||
|
|
f2f56aa310 | ||
|
|
d664549495 | ||
|
|
192cf2e20d | ||
|
|
02a432102d | ||
|
|
f9671ee855 | ||
|
|
b6a410cc17 | ||
|
|
6c6e1caefc | ||
|
|
5c44a77eae | ||
|
|
87a75bafa0 | ||
|
|
fa591c8c2f | ||
|
|
42cedf8ed0 | ||
|
|
def7fb71f1 | ||
|
|
d3ea95b60e | ||
|
|
4479497dfe | ||
|
|
ee69a306a9 | ||
|
|
1761b77d0f | ||
|
|
9f384e016f | ||
|
|
d307d4f698 | ||
|
|
a2570f5568 | ||
|
|
9565152ced | ||
|
|
4dd919f640 | ||
|
|
ab5ddc263b | ||
|
|
5011820f67 | ||
|
|
e315f5d2ac | ||
|
|
be18943fa4 | ||
|
|
b6839fa8f1 | ||
|
|
4fe2289e7f | ||
|
|
e901be28de | ||
|
|
fafbe71b24 | ||
|
|
e1d80c2b93 | ||
|
|
958e5424f0 | ||
|
|
5bd4b6e59d | ||
|
|
f6b738fddd | ||
|
|
239813aec2 | ||
|
|
4041c4964c | ||
|
|
9d641b985a | ||
|
|
1615121b3e | ||
|
|
940eee0562 | ||
|
|
d275fa0111 | ||
|
|
4befb19c35 | ||
|
|
80b49452f4 | ||
|
|
a52ff7372a | ||
|
|
eef2fdb72c | ||
|
|
11c3060682 | ||
|
|
3a651adea5 | ||
|
|
711d8061a6 | ||
|
|
3768912412 | ||
|
|
f5484d4548 | ||
|
|
d957832904 | ||
|
|
682790272f | ||
|
|
4409217521 | ||
|
|
f5bf48baa7 | ||
|
|
842185e45d | ||
|
|
0c6107990d | ||
|
|
9ed6ed69fb | ||
|
|
1d4afd2f6c | ||
|
|
85cb520dd6 | ||
|
|
6f9dcae858 | ||
|
|
a6ffab09df | ||
|
|
9cf9b9b41f | ||
|
|
ef7b53fbc7 | ||
|
|
0e99198fa8 | ||
|
|
ed505fba7c | ||
|
|
5756a7e117 | ||
|
|
71be26a24d | ||
|
|
b7a24da288 | ||
|
|
4dfafd1169 | ||
|
|
e1f9a8e0ce | ||
|
|
4cfc3b3a7f | ||
|
|
94676a6c16 | ||
|
|
d31a3ab088 | ||
|
|
1ff53bc82a | ||
|
|
bc631d926e | ||
|
|
4ef05e2a16 | ||
|
|
f6ae1fedd2 | ||
|
|
541c6772f2 | ||
|
|
c28edaaa96 | ||
|
|
1d59b8bd54 | ||
|
|
995283c15d | ||
|
|
be8c145e84 | ||
|
|
44ca7d9459 | ||
|
|
9bba91fcb8 | ||
|
|
df37e1b7a9 | ||
|
|
103469928e | ||
|
|
de610855fe | ||
|
|
4b8287c097 | ||
|
|
0aac303d51 | ||
|
|
d162c6b0b2 | ||
|
|
8ced55bf46 | ||
|
|
32be6d6816 | ||
|
|
4b5d537eee | ||
|
|
41d31c28d0 | ||
|
|
b66f932b3b | ||
|
|
9cdce06d96 | ||
|
|
fb120be78e | ||
|
|
85920e5b14 | ||
|
|
777ed70669 | ||
|
|
c4daf33a72 | ||
|
|
be642762b3 | ||
|
|
c074731615 | ||
|
|
fee0ec9609 | ||
|
|
0827d59f21 | ||
|
|
f95145b796 | ||
|
|
a8a23fb8eb | ||
|
|
bf34af86ac | ||
|
|
b2d884e329 | ||
|
|
cddacfbb78 | ||
|
|
1eea8c6d8b | ||
|
|
60127cc69d | ||
|
|
5dc8a2dee0 | ||
|
|
5ea19cd539 | ||
|
|
3f3aea37d8 | ||
|
|
4d3ca5a064 | ||
|
|
4331e1ef03 | ||
|
|
8baa95db1c | ||
|
|
a6499e34a3 | ||
|
|
622c852367 | ||
|
|
256046cba7 | ||
|
|
629c1b1686 | ||
|
|
a450089fe7 | ||
|
|
bf119867e8 | ||
|
|
d34359fd49 | ||
|
|
1e92bda18b | ||
|
|
eaf59693b7 | ||
|
|
ae288ffc9b | ||
|
|
aff7c59097 | ||
|
|
983f885b3f | ||
|
|
d8717a4a16 | ||
|
|
86d377722b | ||
|
|
24a140c3af | ||
|
|
7989518eec | ||
|
|
0049a76565 | ||
|
|
4f3188c63f | ||
|
|
d387f198db | ||
|
|
1db29e7674 | ||
|
|
a74ddc7026 | ||
|
|
4efabe9370 | ||
|
|
546d8ba583 | ||
|
|
cf5ecd5a62 | ||
|
|
67113971d9 | ||
|
|
982a449143 | ||
|
|
4086ba93ad | ||
|
|
dad4e3a2f4 | ||
|
|
2bfbf4acaf | ||
|
|
049e3a2646 | ||
|
|
bd83da3331 | ||
|
|
1b0abf4643 | ||
|
|
f56a294760 | ||
|
|
826a9540d0 | ||
|
|
7433a260db | ||
|
|
0d29968169 | ||
|
|
80972aa4ce | ||
|
|
68d31d4ac7 | ||
|
|
fdf88d8355 | ||
|
|
766c19b165 | ||
|
|
5eb39b1ccd | ||
|
|
835c5de073 | ||
|
|
afa508cbed | ||
|
|
ae877ea597 | ||
|
|
8e492a9797 | ||
|
|
60f2d10a50 | ||
|
|
4c5273b8b2 | ||
|
|
6d7b30d77e | ||
|
|
07a2aed52f | ||
|
|
269266127d | ||
|
|
5a0a197355 | ||
|
|
4acd10f2e1 | ||
|
|
e7a7267c9f | ||
|
|
a0a0ef2386 | ||
|
|
95f1681b6c | ||
|
|
14aca37d2e | ||
|
|
dedd24254a | ||
|
|
1e5c94c2c6 | ||
|
|
30a541a5c9 | ||
|
|
85794beb95 | ||
|
|
592b2dcd97 | ||
|
|
f080bd3e68 | ||
|
|
904b8aca8d | ||
|
|
572e5d5f93 | ||
|
|
569c5644fa | ||
|
|
b5a61d949b | ||
|
|
a5e67db604 | ||
|
|
013f4d55c2 | ||
|
|
8e8801b11c | ||
|
|
7f256e5d06 | ||
|
|
08e44f0656 | ||
|
|
eb31bfb78e | ||
|
|
286e041c7f | ||
|
|
c2be54ca7f | ||
|
|
95b6d5d428 | ||
|
|
3a857bb16e | ||
|
|
32f2333ab6 | ||
|
|
9165da14b7 | ||
|
|
73bce1d7d0 | ||
|
|
ab2bb8176c | ||
|
|
719aa67afa | ||
|
|
2d6a00e56f | ||
|
|
1d850c3b7f | ||
|
|
1594225a07 | ||
|
|
503bd53a47 | ||
|
|
ca602199da | ||
|
|
2a003f8bd0 | ||
|
|
1d2d566788 | ||
|
|
4942ba9951 | ||
|
|
1f49985d26 | ||
|
|
e2a63c10a1 | ||
|
|
6f00c98805 | ||
|
|
461c12c7fe | ||
|
|
4dc6e2fb6a | ||
|
|
be003bb5e6 | ||
|
|
70afd1efed | ||
|
|
adf3f74ef2 | ||
|
|
c8b5301267 | ||
|
|
8c3cfa877b | ||
|
|
bde7fe21c4 | ||
|
|
cbeb4984ab | ||
|
|
8048d24857 | ||
|
|
3ed3ef9d71 | ||
|
|
084b4e8c08 | ||
|
|
04066af992 | ||
|
|
e75dfa882b | ||
|
|
ff6c6003fc | ||
|
|
5b1f8bb2a4 | ||
|
|
a385b01d4a | ||
|
|
9a1f54baf1 | ||
|
|
e5db5cb4bb | ||
|
|
5fe2102e26 | ||
|
|
03f8e20dfd | ||
|
|
dfa35e0a0b | ||
|
|
27c4e24fa5 | ||
|
|
54469e9b4d | ||
|
|
9195c813db | ||
|
|
d35b438255 | ||
|
|
a7e1e5f25f | ||
|
|
088fd89294 | ||
|
|
d1b3ca5178 | ||
|
|
1b776f7fdc | ||
|
|
e113253e35 | ||
|
|
e5c62c6730 | ||
|
|
a1157fb49b | ||
|
|
58ef860959 | ||
|
|
159fd5fc59 | ||
|
|
41b420d313 | ||
|
|
584ec3160a | ||
|
|
d51c379514 | ||
|
|
61d6b435b3 | ||
|
|
60e924b62b | ||
|
|
fe4028f588 | ||
|
|
080116cff8 | ||
|
|
4fe5c0dd1d | ||
|
|
9fd2cf35eb | ||
|
|
d5330b4ad4 | ||
|
|
868c72638e | ||
|
|
28252f693b | ||
|
|
0d0cacbf87 | ||
|
|
766cc7fcfa | ||
|
|
5ff8ac46aa | ||
|
|
6bfb76b620 | ||
|
|
868e487a59 | ||
|
|
28044662f4 | ||
|
|
8cd4060334 | ||
|
|
e9ef5b5eac | ||
|
|
027dafe906 | ||
|
|
e7c82a28dd | ||
|
|
305dafe2ad | ||
|
|
1fdaaeef71 | ||
|
|
d7f87051d6 | ||
|
|
8093f034c0 | ||
|
|
a3f14220d3 | ||
|
|
b1f3fdce7a | ||
|
|
f7904af0ed | ||
|
|
d5fafe0ffc | ||
|
|
52d44f0599 | ||
|
|
1632ae61eb | ||
|
|
892f74c2a8 | ||
|
|
bef62ddc71 | ||
|
|
2e23721774 | ||
|
|
837422145d | ||
|
|
97df10e6b8 | ||
|
|
65e004f145 | ||
|
|
e96c0b1127 | ||
|
|
f71eb5a169 | ||
|
|
5b90c433ab | ||
|
|
00d194d5d1 | ||
|
|
f3c183b563 | ||
|
|
ffe029932d | ||
|
|
f78cb09694 | ||
|
|
84f5842c81 | ||
|
|
432bb8666a | ||
|
|
f6449687b5 | ||
|
|
fe29e58f22 | ||
|
|
fa0216cd6d | ||
|
|
f2068a10e4 | ||
|
|
1c7019a04d | ||
|
|
224d526c84 | ||
|
|
939f974d19 | ||
|
|
019fa11616 | ||
|
|
d7b1224628 | ||
|
|
b29e2c1808 | ||
|
|
8269ec225d | ||
|
|
90e9e5e7fd | ||
|
|
00973d4a14 | ||
|
|
c635846dba | ||
|
|
8437aa894d | ||
|
|
f15d476889 | ||
|
|
13847fc155 | ||
|
|
7484656be3 | ||
|
|
d5849c39f3 | ||
|
|
95a9ded28f | ||
|
|
5f2def488e | ||
|
|
0506dbb30e | ||
|
|
0e9c6db759 | ||
|
|
daa0ab51a6 | ||
|
|
daecc1aa76 | ||
|
|
f49f1b4755 | ||
|
|
f27ccf4212 | ||
|
|
51cbc653af | ||
|
|
e2555e2448 | ||
|
|
be3743e227 | ||
|
|
185b206076 | ||
|
|
f86a5b5fcd | ||
|
|
83071d85e3 | ||
|
|
65337bc9e9 | ||
|
|
e7b9ba70b6 | ||
|
|
dee4f5c517 | ||
|
|
72e112ade8 | ||
|
|
788abad42e | ||
|
|
e5ad8d63f0 | ||
|
|
e5b3d74515 | ||
|
|
bd1d9834be | ||
|
|
d0aedf33ba | ||
|
|
0316710ee4 | ||
|
|
e7e8c53d89 | ||
|
|
d64effc74f | ||
|
|
98bdcfe04e | ||
|
|
af782fe067 | ||
|
|
e9b595bdaa | ||
|
|
fe87b754c7 | ||
|
|
0fdfdcf09c | ||
|
|
b6eef3a886 | ||
|
|
02ab2b8a63 | ||
|
|
e17188e52c | ||
|
|
99a6d7044c | ||
|
|
c4ad832d67 | ||
|
|
d625eb6508 | ||
|
|
bc66efa1a5 | ||
|
|
bbafadcbf6 | ||
|
|
99abb200d6 | ||
|
|
5542e5b04c | ||
|
|
29babb6015 | ||
|
|
367eb4f6d7 | ||
|
|
4210fdcb27 | ||
|
|
4d2e5c3e94 | ||
|
|
778751160b | ||
|
|
e9ef7c0ecd | ||
|
|
9ff918ac6b | ||
|
|
88a19f4bd3 | ||
|
|
6a44e82e33 | ||
|
|
9d9f96ac1e | ||
|
|
0a4b582dd9 | ||
|
|
4250234231 | ||
|
|
75c029c462 | ||
|
|
910c033d2a | ||
|
|
92778baf99 | ||
|
|
fc8c40c62f | ||
|
|
380a45f987 | ||
|
|
3ffc31ba58 | ||
|
|
f41d388bf5 | ||
|
|
8f3f00fe00 | ||
|
|
77fccc950a | ||
|
|
0733909d04 | ||
|
|
702710aab1 | ||
|
|
2fc22998cf | ||
|
|
f93099cbb6 | ||
|
|
608f1532a6 | ||
|
|
a247cc614f | ||
|
|
17d7bcca78 | ||
|
|
8ddbde62c1 | ||
|
|
9b2db20b3b | ||
|
|
22b00c019d | ||
|
|
690c275ee2 | ||
|
|
a229eac2cc | ||
|
|
c23c5c686c | ||
|
|
26b1a94d22 | ||
|
|
5af7dc943f | ||
|
|
5958577a6e | ||
|
|
de39663095 | ||
|
|
00eeebbf04 | ||
|
|
1313a30563 | ||
|
|
11caeaba11 | ||
|
|
36f59375da | ||
|
|
0bb3aa6c9d | ||
|
|
49c0c2d996 | ||
|
|
3a2d6da052 | ||
|
|
643b2f1bb4 | ||
|
|
1a9c4e3756 | ||
|
|
5a9525fb89 | ||
|
|
5e75fca33f | ||
|
|
187bf0165d | ||
|
|
f794eea2e6 | ||
|
|
81fc5ee6b3 | ||
|
|
d851733f6a | ||
|
|
57bbf346aa | ||
|
|
62f63923c1 | ||
|
|
737a0ab6d7 | ||
|
|
da9133ae85 | ||
|
|
4cee87273c | ||
|
|
676709e706 | ||
|
|
68df98eaaa | ||
|
|
77e8ffe500 | ||
|
|
12b1ecfcf9 | ||
|
|
120216f715 | ||
|
|
0c99ceda92 | ||
|
|
7a9b25889b | ||
|
|
4bb44ad315 | ||
|
|
b83a215885 | ||
|
|
6c10a75c6a | ||
|
|
b6321a8bed | ||
|
|
3e69d8203e | ||
|
|
73c144a75f | ||
|
|
052d35fe59 | ||
|
|
55985c47d4 | ||
|
|
ae2100244f | ||
|
|
207941558f | ||
|
|
3281b8bbd4 | ||
|
|
0bae88311e | ||
|
|
3da4b01f4e | ||
|
|
a55b149d6f | ||
|
|
a883d3728f | ||
|
|
2fbc40e966 | ||
|
|
5c6caf5603 | ||
|
|
2bb8854f61 | ||
|
|
1e0d8d2efb | ||
|
|
146793c42c | ||
|
|
91f16121d1 | ||
|
|
5d32ddb408 | ||
|
|
e5271dbda9 | ||
|
|
6c2fbec22b | ||
|
|
0c1222f0f5 | ||
|
|
fbbb150e86 | ||
|
|
2117fa53f0 | ||
|
|
1d3bd4e26a | ||
|
|
e0b9f57d85 | ||
|
|
f6c586986d | ||
|
|
757a3f8651 | ||
|
|
637a97c4de | ||
|
|
f8bd3299a3 | ||
|
|
6c1e2ae537 | ||
|
|
1b490233b6 | ||
|
|
d511c2d2ad | ||
|
|
65b4c9f931 | ||
|
|
b1197d581a | ||
|
|
c634d12ef4 | ||
|
|
b79fe964b8 | ||
|
|
d45464c6c8 | ||
|
|
7880adda3f | ||
|
|
c97fea37c0 | ||
|
|
09830aecb3 | ||
|
|
4150895401 | ||
|
|
7d5f68a7bf | ||
|
|
84da2eee8c | ||
|
|
eeb6ee0821 | ||
|
|
912933d4c8 | ||
|
|
c1df8cc5ea | ||
|
|
258ba6e7b4 | ||
|
|
5e1bb655dc | ||
|
|
69d42fc980 | ||
|
|
3f9a3f72c8 | ||
|
|
4a4cf77fd6 | ||
|
|
029fddceb0 | ||
|
|
f9400a6670 | ||
|
|
8cc768cc50 | ||
|
|
a847064093 | ||
|
|
3b802e9ba6 | ||
|
|
85cd2c1472 | ||
|
|
b575df867e | ||
|
|
8c6ef5b26f | ||
|
|
895e12c29b | ||
|
|
4969ceadd4 | ||
|
|
7b0b0ef91a | ||
|
|
80ce2b2464 | ||
|
|
ef65503173 | ||
|
|
adf4465c27 | ||
|
|
3bbbca87d6 | ||
|
|
651ccef328 | ||
|
|
44cc33284f | ||
|
|
384543c553 | ||
|
|
69846749bf | ||
|
|
9524b06d26 | ||
|
|
0533339023 | ||
|
|
582b95915a | ||
|
|
46c91899e7 | ||
|
|
e850be40b2 | ||
|
|
54d14a5151 | ||
|
|
0159079608 | ||
|
|
c38f451c78 | ||
|
|
426f50c69c | ||
|
|
5e6b8ec506 | ||
|
|
5bde5bee16 | ||
|
|
aceb631aac | ||
|
|
7abb426e3f | ||
|
|
c2d3544c55 | ||
|
|
ba0be549e3 | ||
|
|
9caeb62695 | ||
|
|
f39ed010c7 | ||
|
|
792d22cf74 | ||
|
|
039dde600f | ||
|
|
0eb5a1c61f | ||
|
|
aee87e3d01 | ||
|
|
f3fac22bab | ||
|
|
b99f5b0663 | ||
|
|
1bd55c8d69 | ||
|
|
7c3732e277 | ||
|
|
7f236ed2bb | ||
|
|
80502ff937 | ||
|
|
2956d5f3eb | ||
|
|
398c3eec29 | ||
|
|
fea3580c1c | ||
|
|
a0910a7f58 | ||
|
|
87cc201811 | ||
|
|
e4d186b9a7 | ||
|
|
de0b88737e | ||
|
|
0ddd2f6c77 | ||
|
|
067858ae10 | ||
|
|
ec46fae228 | ||
|
|
4de148f783 | ||
|
|
6fbbc2bea5 | ||
|
|
e8b63f8c9d | ||
|
|
3166be9da0 | ||
|
|
6d9f43be5d | ||
|
|
fdc5fea887 | ||
|
|
92e3c79812 | ||
|
|
fb55f8cdd1 | ||
|
|
cde374b505 | ||
|
|
478a92a881 | ||
|
|
85ce646e0c | ||
|
|
ef4ffcf3a2 | ||
|
|
bf533be331 | ||
|
|
81aa611c74 | ||
|
|
66ffe200d8 | ||
|
|
c33a5580fe | ||
|
|
7ec9aef2ed | ||
|
|
5b240a867e | ||
|
|
304e72c7e2 | ||
|
|
8fe71531d6 | ||
|
|
2badad6da5 | ||
|
|
395beb39bc | ||
|
|
e5cae42356 | ||
|
|
fe82f05462 | ||
|
|
c9245748bf | ||
|
|
67c7250b3c | ||
|
|
29e5acb29e | ||
|
|
78f6748fb0 | ||
|
|
7c797515e6 | ||
|
|
044078f7e3 | ||
|
|
eb50137ee2 | ||
|
|
9b5db34669 | ||
|
|
9e427d7833 | ||
|
|
96134f2a5e | ||
|
|
2f08ec0926 | ||
|
|
01f77fa993 | ||
|
|
98747c949d | ||
|
|
2bd217b3d2 | ||
|
|
e80e65946b | ||
|
|
89afad1a32 | ||
|
|
e31c7f7740 | ||
|
|
c42bf2d8da | ||
|
|
a983e792d8 | ||
|
|
215c02ac47 | ||
|
|
6e8a4cc97b | ||
|
|
360054f500 | ||
|
|
fd9d300654 | ||
|
|
ca318c1842 | ||
|
|
82141b7387 | ||
|
|
ffe525c5cc | ||
|
|
12b6e671b0 | ||
|
|
9985af45fb | ||
|
|
81ddc99f7b | ||
|
|
00a4929301 | ||
|
|
9ad270ec14 | ||
|
|
19eb6c7e50 | ||
|
|
e04b9fc136 | ||
|
|
ed79d4dffb | ||
|
|
6c9e805682 | ||
|
|
939b5a00f5 | ||
|
|
95d322dc36 | ||
|
|
192617c9ac | ||
|
|
5fb6d2fa6a | ||
|
|
fab87c7f00 | ||
|
|
0e2e7395a9 | ||
|
|
5fd0de842f | ||
|
|
6d5aec39fe | ||
|
|
b0d6e905f1 | ||
|
|
c20d46fef3 | ||
|
|
700d7bc6aa | ||
|
|
3b39b3a88d | ||
|
|
6c220467b8 | ||
|
|
9893bc166a | ||
|
|
a593d75404 | ||
|
|
d039671ffb | ||
|
|
776dc84475 | ||
|
|
fca8e0ebee | ||
|
|
c1c9ebd381 | ||
|
|
c9904f38d3 | ||
|
|
64926c7f4c | ||
|
|
4c3b477b44 | ||
|
|
d851115701 | ||
|
|
bdd67b0127 | ||
|
|
e7e1852b21 | ||
|
|
89f18f36b5 | ||
|
|
620d864d17 | ||
|
|
0b767c15a6 | ||
|
|
587a8b5b7b | ||
|
|
78cd9ea71a | ||
|
|
af00eeaf9a | ||
|
|
13fa712cc5 | ||
|
|
9fe4a295ff | ||
|
|
43b02c6f13 | ||
|
|
bb2b46cdc1 | ||
|
|
3df89e65ca | ||
|
|
90af882aa6 | ||
|
|
9c59deca1d | ||
|
|
d0c3f52ab6 | ||
|
|
1043cc4fc1 | ||
|
|
4a73b26821 | ||
|
|
f97280d071 | ||
|
|
d834091524 | ||
|
|
0e7f9cf2ad | ||
|
|
a4536d45a1 | ||
|
|
edbe0bd0e6 | ||
|
|
dab4c004d3 | ||
|
|
9f07cb0cc4 | ||
|
|
f388f3801a | ||
|
|
e416f9b017 | ||
|
|
ef144dbc24 | ||
|
|
5e5ca0ba16 | ||
|
|
d380cb8c47 | ||
|
|
5d6fff860e | ||
|
|
d28afc21a6 | ||
|
|
ce42915ce5 | ||
|
|
696c4bfde3 | ||
|
|
908ba51144 | ||
|
|
3da21ff2fc | ||
|
|
cdb54bcc72 | ||
|
|
832e42c881 | ||
|
|
3af85b7e82 | ||
|
|
98c8896588 | ||
|
|
f4c77a558f | ||
|
|
626e4b1b8a | ||
|
|
b72dcdeeb4 | ||
|
|
182db2b2e3 | ||
|
|
041066520f | ||
|
|
421db5b224 | ||
|
|
948049f944 | ||
|
|
2da7da0558 | ||
|
|
4260d01bb9 | ||
|
|
937674a373 | ||
|
|
183bb2e92d | ||
|
|
7a2fcf4165 | ||
|
|
8f67743834 | ||
|
|
717abccd10 | ||
|
|
eca5aa3c2b | ||
|
|
1af709ea64 | ||
|
|
0eb994e625 | ||
|
|
0123bba1fb | ||
|
|
4f2c46ac36 | ||
|
|
105e5fe71c | ||
|
|
20bbbfc493 | ||
|
|
640b5abe1e | ||
|
|
9fd869d917 | ||
|
|
554be364d6 | ||
|
|
70fe8cdd77 | ||
|
|
b98bbf482a | ||
|
|
efbe21d433 | ||
|
|
7d3101e077 | ||
|
|
6544ab4872 | ||
|
|
8b0aa634ec | ||
|
|
ae0919a3d5 | ||
|
|
f6868318e0 | ||
|
|
f0626d2ee4 | ||
|
|
59f33cbb51 | ||
|
|
6fc7f9b384 | ||
|
|
8688120df1 | ||
|
|
25fc9c2dc4 | ||
|
|
6938490328 | ||
|
|
53e6a3e312 | ||
|
|
381a26fa4c | ||
|
|
5ef11b44bf | ||
|
|
c5ee4d7eaf | ||
|
|
9806b395f1 | ||
|
|
75c4662c0b | ||
|
|
1d382cc542 | ||
|
|
b86f7689ec | ||
|
|
a43d03d8aa | ||
|
|
c4e5ff4c94 | ||
|
|
9cea1270c3 | ||
|
|
9269fb0dfe | ||
|
|
2cbd4d6cc5 | ||
|
|
36b45fd8f4 | ||
|
|
38b39a881e | ||
|
|
c2dcd507b8 | ||
|
|
7f0d4e6c36 | ||
|
|
1d7c25354a | ||
|
|
79b57cf696 | ||
|
|
022e992041 | ||
|
|
22038daa0e | ||
|
|
e6432df04b | ||
|
|
35fc397ecc | ||
|
|
e0267da7ac | ||
|
|
a7ef1367b8 | ||
|
|
75e80a210c | ||
|
|
704fd8693c | ||
|
|
184b1f2ddb | ||
|
|
ec5a0b8cde | ||
|
|
eb2f949175 | ||
|
|
ecf5ba26ca | ||
|
|
953d338966 | ||
|
|
7aeedb90bf | ||
|
|
ca6ee798d6 | ||
|
|
4bdad25a12 | ||
|
|
f10914156f | ||
|
|
1a1f33ef57 | ||
|
|
d6fa54bd1e | ||
|
|
a1194d91f3 | ||
|
|
4eedd443a5 | ||
|
|
20ca55f51e | ||
|
|
85b714857f | ||
|
|
a315f43039 | ||
|
|
07d65a3232 | ||
|
|
40781d45a0 | ||
|
|
31241f5d12 | ||
|
|
ce73b44ccf | ||
|
|
364da9d2f0 | ||
|
|
0124d2c121 | ||
|
|
3abd8bc565 | ||
|
|
34dda74176 | ||
|
|
6d235e7531 | ||
|
|
8b864324de | ||
|
|
319e5ac034 | ||
|
|
a9a1e849d3 | ||
|
|
71206a9886 | ||
|
|
0ce6deedbe | ||
|
|
526705d440 | ||
|
|
c8ac990436 | ||
|
|
668ebe4f41 | ||
|
|
21d40f4849 | ||
|
|
0d016dea64 | ||
|
|
48b3d82518 | ||
|
|
dc36f05ee7 | ||
|
|
b757951f9c | ||
|
|
b6495ff006 | ||
|
|
963ce8198e | ||
|
|
3f568f732e | ||
|
|
ef7f919fa1 | ||
|
|
947e4190d5 | ||
|
|
edb4ca6a63 | ||
|
|
e0a58a5c98 | ||
|
|
cd058633f5 | ||
|
|
016de02356 | ||
|
|
f494ec488f | ||
|
|
09e5e06614 | ||
|
|
910c37729d | ||
|
|
a698dfde8b | ||
|
|
19a5945730 | ||
|
|
16024e0020 | ||
|
|
10b0277120 | ||
|
|
f28248037c | ||
|
|
2ef98752dc | ||
|
|
101e6cb539 | ||
|
|
9c0da69dbb | ||
|
|
d865b5adee | ||
|
|
4809520462 | ||
|
|
3e23759e69 | ||
|
|
4cd0f4ea1e | ||
|
|
4822e326f2 | ||
|
|
b837fe7233 | ||
|
|
cb50220b60 | ||
|
|
98724c2acd | ||
|
|
a707fa0385 | ||
|
|
a059ec11e7 | ||
|
|
6d17d7f0f6 | ||
|
|
b41aaefc7e | ||
|
|
fd6a3ed30c | ||
|
|
87e3916568 | ||
|
|
3beacf0263 | ||
|
|
03fade9501 | ||
|
|
0cd1da0680 | ||
|
|
9fc800c45a | ||
|
|
0a9a84e357 | ||
|
|
6ac236ed22 | ||
|
|
3e34e42c93 | ||
|
|
aa2a6d7f11 | ||
|
|
0848815485 | ||
|
|
4f257f9e7e | ||
|
|
7132e3a7f6 | ||
|
|
ddd15921a3 | ||
|
|
256f83b7ea | ||
|
|
b19b9d450f | ||
|
|
6507bf9fad | ||
|
|
812cd9be76 | ||
|
|
9665d3a106 | ||
|
|
342c4a3253 | ||
|
|
5652e2d94f | ||
|
|
834e4e03e3 | ||
|
|
7d3829e35f | ||
|
|
7207d6ab7a | ||
|
|
ed81ff1263 | ||
|
|
f46327d206 | ||
|
|
cf1c9452c1 | ||
|
|
58ead53492 | ||
|
|
8450a0c213 | ||
|
|
15939bb155 | ||
|
|
14164e7a19 | ||
|
|
83f238867f | ||
|
|
119344031a | ||
|
|
3e8da28e70 | ||
|
|
28100545b5 | ||
|
|
fafef03512 | ||
|
|
801898a9e2 | ||
|
|
349833573f | ||
|
|
e4c1188498 | ||
|
|
33ab9ada96 | ||
|
|
0892669566 | ||
|
|
a9124e45b2 | ||
|
|
6ac2493445 | ||
|
|
30e18d43a2 | ||
|
|
b271a5349b | ||
|
|
f7b7a43f7d | ||
|
|
53d6961529 | ||
|
|
f01a473b4e | ||
|
|
000f377845 | ||
|
|
72068d5a93 | ||
|
|
c442a5bce2 | ||
|
|
61a852affd | ||
|
|
c22c28bc67 | ||
|
|
01fb4045bd | ||
|
|
1f17fb34e4 | ||
|
|
0895a25615 | ||
|
|
e23752d4f3 | ||
|
|
80e60d9d95 | ||
|
|
b4b3cc6e26 | ||
|
|
6918e8fdd7 | ||
|
|
e2444734fd | ||
|
|
ee80bf7ca7 | ||
|
|
d4d20961e3 | ||
|
|
e9c29a4812 | ||
|
|
9665ec09e7 | ||
|
|
80d57521aa | ||
|
|
5eaabe316c | ||
|
|
af1d1dd7a2 | ||
|
|
f18707c3a0 | ||
|
|
b7e9d4894e | ||
|
|
90e05fec87 | ||
|
|
0fd027b957 | ||
|
|
4acd4a03d3 | ||
|
|
a342a77caf | ||
|
|
50d20f7e84 | ||
|
|
e24d789510 | ||
|
|
fb3ba9a475 | ||
|
|
129c69ddac | ||
|
|
cb8688a8c3 | ||
|
|
eb0b33bfcd | ||
|
|
1d667dadad | ||
|
|
4604578864 | ||
|
|
4c310a4c36 | ||
|
|
2634c5ae19 | ||
|
|
97b261284f | ||
|
|
85bf2f77e0 | ||
|
|
2f2f96d2f3 | ||
|
|
bdb64031f3 | ||
|
|
a08c6f8235 | ||
|
|
51f2cb8a64 | ||
|
|
780134d18d | ||
|
|
0fcdc800e1 | ||
|
|
ecb8717672 | ||
|
|
858d1a7d66 | ||
|
|
c4496e6d43 | ||
|
|
4590c464ef | ||
|
|
477dbbb909 | ||
|
|
95c5d7eb39 | ||
|
|
39434c3fec | ||
|
|
d5d5c95507 | ||
|
|
c4796bedd9 | ||
|
|
713ca7d55a | ||
|
|
e8a8069a67 | ||
|
|
02f54eca65 | ||
|
|
27af4e3b7c | ||
|
|
5a7655ce06 | ||
|
|
5b1a512e1e | ||
|
|
3ad6ca0992 | ||
|
|
12ee240695 | ||
|
|
9113df9ba3 | ||
|
|
cc124bf211 | ||
|
|
c63feef171 | ||
|
|
a04a968def | ||
|
|
3b6098b5e8 | ||
|
|
5294f86595 | ||
|
|
8bc570b0a5 | ||
|
|
7105e6c3e8 | ||
|
|
ea544efba8 | ||
|
|
9e0e2f8e77 | ||
|
|
05c9640658 | ||
|
|
8ed9ed853d | ||
|
|
04b9311190 | ||
|
|
37530d30df | ||
|
|
ea51786192 | ||
|
|
c7780c5fe6 | ||
|
|
1362902082 | ||
|
|
e55faff0a1 | ||
|
|
0b42a564b0 | ||
|
|
a861b174a2 | ||
|
|
5c08e066aa | ||
|
|
34b3218d83 | ||
|
|
614985c837 | ||
|
|
ccabbc4fdf | ||
|
|
6df57b7975 | ||
|
|
d5028e601e | ||
|
|
fed981681a | ||
|
|
bcd05bf5a7 | ||
|
|
683a691bf9 | ||
|
|
06603e53ca | ||
|
|
eabc90ec49 | ||
|
|
db715f3e7d | ||
|
|
f63c740917 | ||
|
|
2c9b749f72 | ||
|
|
5ea0816540 | ||
|
|
5d16c39d5f | ||
|
|
8e1dc84a48 | ||
|
|
29d448c690 | ||
|
|
f0c61bc4a7 | ||
|
|
b6b2961d4e | ||
|
|
4b5747c730 | ||
|
|
1896193718 | ||
|
|
f4f178d3bf | ||
|
|
8639091d06 | ||
|
|
ae2826ad1f | ||
|
|
a847a37a83 | ||
|
|
78daf314bd | ||
|
|
ddcb68c944 | ||
|
|
c3d04848f4 | ||
|
|
c5c3b2ecf0 | ||
|
|
d0c8563fbf | ||
|
|
53c784a6fb | ||
|
|
687ba70f77 | ||
|
|
0d26185c38 | ||
|
|
6b26e36c17 | ||
|
|
73bae25a3f | ||
|
|
26b6584a39 | ||
|
|
4e3bfc81b6 | ||
|
|
272c62e893 | ||
|
|
0ce1e5fa51 | ||
|
|
3bb836a101 | ||
|
|
646bbdf489 | ||
|
|
4ee3385f53 | ||
|
|
007ae1fcfc | ||
|
|
ebdef8afa7 | ||
|
|
2386381d03 | ||
|
|
d9c6f892c7 | ||
|
|
eb1fdcec73 | ||
|
|
c47e76fd50 | ||
|
|
432bfe96ac | ||
|
|
214018be4b | ||
|
|
b16053bbdf | ||
|
|
efa4824db6 | ||
|
|
e5a9195e66 | ||
|
|
9f5ae4d628 | ||
|
|
6cd1b94935 | ||
|
|
a0c0a7439e | ||
|
|
2e00fd5d4f | ||
|
|
a85a471d3b | ||
|
|
6600f08059 | ||
|
|
07b62cfbd3 | ||
|
|
505dd333c4 | ||
|
|
5900387237 | ||
|
|
8fe365d66e | ||
|
|
94ad09b53f | ||
|
|
f7da33a873 | ||
|
|
dab844308b | ||
|
|
b9a26efa80 | ||
|
|
dccb9b7291 | ||
|
|
0438e830b0 | ||
|
|
d5997d92c3 | ||
|
|
7b5cee8da9 | ||
|
|
d647bdaf9e | ||
|
|
67f38d0644 | ||
|
|
4bc022736c | ||
|
|
0129209eb0 | ||
|
|
39a39a8603 | ||
|
|
3ba8ee4ec8 | ||
|
|
b2023020e0 | ||
|
|
5e6c288c2b | ||
|
|
5a58312b53 | ||
|
|
bc786cdb86 | ||
|
|
0897cd0fe4 | ||
|
|
3fc2e70007 | ||
|
|
054516d5c5 | ||
|
|
7ea521665c | ||
|
|
e16cbe6a11 | ||
|
|
d1b5c69ba1 | ||
|
|
8fcb091f37 | ||
|
|
ce28dd8201 | ||
|
|
8da011ad78 | ||
|
|
1273f4c9b7 | ||
|
|
d2ec87de2c | ||
|
|
e4a4b0165f | ||
|
|
0a3a69b7dc | ||
|
|
69ed468ec3 | ||
|
|
b17f537a4c | ||
|
|
02b0ed118f | ||
|
|
594b66c18a | ||
|
|
bc7ec3854f | ||
|
|
63f95c0a5c | ||
|
|
77c72970e8 | ||
|
|
4ba8e725ea | ||
|
|
e54125a6f5 | ||
|
|
b1cb6ab9bc | ||
|
|
190d9684f0 | ||
|
|
1d4866c5b5 | ||
|
|
b9148d380b | ||
|
|
a87724ed1a | ||
|
|
3ee3954043 | ||
|
|
f6a0949d14 | ||
|
|
fe8295c2ec | ||
|
|
422cedf718 | ||
|
|
9b3e333134 | ||
|
|
dcdfcce3fe | ||
|
|
2d1741504a | ||
|
|
5fa7d34757 | ||
|
|
faaf233153 | ||
|
|
7591f68970 | ||
|
|
6c04550558 | ||
|
|
5666002ff2 | ||
|
|
cee77ef5a1 | ||
|
|
67d46e8d05 | ||
|
|
97a957e17a | ||
|
|
69066cfc5e | ||
|
|
1e95346205 | ||
|
|
574a993f59 | ||
|
|
5785212d28 | ||
|
|
63c02677a0 | ||
|
|
b96b2ea2fb | ||
|
|
3da3e2100c | ||
|
|
22f0468c8e | ||
|
|
691b9ef417 | ||
|
|
f4d916a4d2 | ||
|
|
c58dd9f741 | ||
|
|
fc3376cbe4 | ||
|
|
f0bce98d91 | ||
|
|
8c5948950d | ||
|
|
e6ccfb6dd7 | ||
|
|
177354407a | ||
|
|
b8f6393df6 | ||
|
|
2b3dc48bf1 | ||
|
|
2534e9ad8f | ||
|
|
087b7df52a | ||
|
|
6df1d35712 | ||
|
|
677121edca | ||
|
|
471cac8187 | ||
|
|
ce334b9518 | ||
|
|
8620b1f3a0 | ||
|
|
e5a63a5078 | ||
|
|
8ec8843a09 | ||
|
|
6cf3cdcadb | ||
|
|
4b50fe3f15 | ||
|
|
ce5c8d6817 | ||
|
|
3e2a7e2941 | ||
|
|
883207240c | ||
|
|
92783527c2 | ||
|
|
001e12b274 | ||
|
|
6039ee0b0f | ||
|
|
f50cd14bd8 | ||
|
|
7a2f90b5bf | ||
|
|
32a8232689 | ||
|
|
848c36aef4 | ||
|
|
938f8394e0 | ||
|
|
3f9bbd5fc7 | ||
|
|
583de3a505 | ||
|
|
60c05fad25 | ||
|
|
ad0e856e1b | ||
|
|
cdbfe4ae10 | ||
|
|
1974c422ce | ||
|
|
3da244b3dd | ||
|
|
f36fccfdea | ||
|
|
f54b6b2ebc | ||
|
|
47db8f5bf1 | ||
|
|
79eb351381 | ||
|
|
99b95cfb74 | ||
|
|
3ab275201a | ||
|
|
56282fb2fa | ||
|
|
951ed70fb0 | ||
|
|
889eaff418 | ||
|
|
6b5a0cbd4f | ||
|
|
6db0e3ac3c | ||
|
|
ccfd104545 | ||
|
|
4756b44cbe | ||
|
|
74969ed606 | ||
|
|
90dae04fb7 | ||
|
|
33152c7bb1 | ||
|
|
98cdb38c49 | ||
|
|
9a1ece5f13 | ||
|
|
aa96ede848 | ||
|
|
3a5db3e45f | ||
|
|
44970cc628 | ||
|
|
793fbff7f5 | ||
|
|
f6e7d00b1e | ||
|
|
8cacf91e09 | ||
|
|
25d9c8e501 | ||
|
|
6dade9e758 | ||
|
|
cab456aaff | ||
|
|
d97c3a09ca | ||
|
|
0389be522e | ||
|
|
55450a5ebc | ||
|
|
7f007c2ff4 | ||
|
|
8282a9769d | ||
|
|
a16cd37f1a | ||
|
|
c531213a5f | ||
|
|
82bea82d94 | ||
|
|
790276fced | ||
|
|
d78795e7bd | ||
|
|
dca0b211f6 | ||
|
|
bbeeb356ab | ||
|
|
3b7ac045d7 | ||
|
|
7f0507473d | ||
|
|
281eb2fafe | ||
|
|
a679f2f767 | ||
|
|
f904698d66 | ||
|
|
a320e220c3 | ||
|
|
57048400dc | ||
|
|
593c62116c | ||
|
|
f945ab79ec | ||
|
|
8307b2148f | ||
|
|
8e26f93211 | ||
|
|
60a4563908 | ||
|
|
78f65401e8 | ||
|
|
31bd5cbeda | ||
|
|
e25fbd1c2a | ||
|
|
b12fc12caa | ||
|
|
9ccffe9af2 | ||
|
|
6dcfbe2e17 | ||
|
|
51e6d024ab | ||
|
|
e6e126dff4 | ||
|
|
a373297a6b | ||
|
|
9667ee28d1 | ||
|
|
bc73b7bbf0 | ||
|
|
e44efb43d1 | ||
|
|
9d440552bd | ||
|
|
c85363f355 | ||
|
|
ddabb540c1 | ||
|
|
6a22768c9c | ||
|
|
035e72edf7 | ||
|
|
f15f8e7bf0 | ||
|
|
8513bb2dbe | ||
|
|
fd1068a600 | ||
|
|
7159f9c187 | ||
|
|
c6e614d896 | ||
|
|
064d538023 | ||
|
|
c30189f903 | ||
|
|
deb5164eb3 | ||
|
|
26a2569b8e | ||
|
|
c04734048f | ||
|
|
38ad6fe1cc | ||
|
|
10b8b4b4f6 | ||
|
|
292037f90b | ||
|
|
ec3c2628b1 | ||
|
|
9f3dd54198 | ||
|
|
e7298b13b0 | ||
|
|
625608804e | ||
|
|
3149a298a7 | ||
|
|
002b30d110 | ||
|
|
b434dfb6b8 | ||
|
|
756d0bbd5b | ||
|
|
fc38771d69 | ||
|
|
b1dfd94d49 | ||
|
|
65d09705ac | ||
|
|
55a2e52764 | ||
|
|
5d7fb56d90 | ||
|
|
2b131eec54 | ||
|
|
7c9d8fcf64 | ||
|
|
7844a74c1a | ||
|
|
47b206c445 | ||
|
|
c3205b8266 | ||
|
|
59e7e5caa7 | ||
|
|
9cfefe58bf | ||
|
|
cb6a186c5d | ||
|
|
85bfdd8f54 | ||
|
|
5e9acf59e4 | ||
|
|
b765ad603d | ||
|
|
4b01ff3f51 | ||
|
|
e0a1f4b8fe | ||
|
|
d4b270e527 | ||
|
|
0d3c1cba9a | ||
|
|
25590cc210 | ||
|
|
c3372e4a1f | ||
|
|
22f05b4cf8 | ||
|
|
4645fcd0c8 | ||
|
|
bd7395cb44 | ||
|
|
d3308a4d42 | ||
|
|
ceb11f839f | ||
|
|
dabaa49f8d | ||
|
|
714bbc0af0 | ||
|
|
ea4b177b83 | ||
|
|
a0ca202727 | ||
|
|
01a57d91ad | ||
|
|
7fb74027d4 | ||
|
|
11c8fccd88 | ||
|
|
531d57feff | ||
|
|
2d82eed4be | ||
|
|
b1348dbdd8 | ||
|
|
36903d6358 | ||
|
|
79560c13e6 | ||
|
|
e068bcdbc1 | ||
|
|
ee875a9320 | ||
|
|
94acd32d69 | ||
|
|
17e41eee1d | ||
|
|
a92f96bf80 | ||
|
|
1eee0baa28 | ||
|
|
6c971f6ece | ||
|
|
d1f6cb31d6 | ||
|
|
6fcd3757fe | ||
|
|
da73dc7fcb | ||
|
|
e780c90dd6 | ||
|
|
7784422f84 | ||
|
|
b24aec82e7 | ||
|
|
00441885d2 | ||
|
|
e989735e89 | ||
|
|
ad417731ce | ||
|
|
66ff297103 | ||
|
|
9881c4cc73 | ||
|
|
e09cb3a1cb | ||
|
|
464738274d | ||
|
|
cff0fd9476 | ||
|
|
0fd55cb13c | ||
|
|
3ceff5aa71 | ||
|
|
dd00c58b08 | ||
|
|
0304a57915 | ||
|
|
3fe22b7ed0 | ||
|
|
afa71bcaf3 | ||
|
|
228bedd6d0 | ||
|
|
b2b714f713 | ||
|
|
eb1476b28b | ||
|
|
fa165a735b | ||
|
|
0d57c67cc0 | ||
|
|
77958b465e | ||
|
|
6af467398b | ||
|
|
c5a8e470c5 | ||
|
|
2205ecd9c6 | ||
|
|
7952dac85e | ||
|
|
e70eb61717 | ||
|
|
2bde7aa02b | ||
|
|
146cb6a0d8 | ||
|
|
3890dea35b | ||
|
|
a5bc07c9e4 | ||
|
|
fe0b778fac | ||
|
|
f03510ef6b | ||
|
|
ca25d329ae | ||
|
|
c1cdad5566 | ||
|
|
926d1c6459 | ||
|
|
896f07fdd4 | ||
|
|
d4ba9441b6 | ||
|
|
0d81918685 | ||
|
|
7673fb44e5 | ||
|
|
dbd7afeda0 | ||
|
|
a904057a58 | ||
|
|
7895ac6c40 | ||
|
|
74a5bd2556 | ||
|
|
28d9e15980 | ||
|
|
30c157c23e | ||
|
|
e104f553cb | ||
|
|
f09cce93ee | ||
|
|
c9cae9479d | ||
|
|
2a750eaa73 | ||
|
|
61ab41bf86 | ||
|
|
278afd77c0 | ||
|
|
399df17a3d | ||
|
|
c1c3a7c7ae | ||
|
|
e4392bdbfb | ||
|
|
24875e352d | ||
|
|
cb6fc7f618 | ||
|
|
5f990ecb6d | ||
|
|
7019be987c | ||
|
|
9819088563 | ||
|
|
66539bbe3a | ||
|
|
27f69e3c09 | ||
|
|
f0d66985f7 | ||
|
|
753aebccab | ||
|
|
db9faebcc9 | ||
|
|
7c2ecaec4b | ||
|
|
e63e79d3f9 | ||
|
|
acd5dfc23f | ||
|
|
fdac3bfe4a | ||
|
|
3e43424129 | ||
|
|
b28d3ce302 | ||
|
|
4d482ebb81 | ||
|
|
bc9e928187 | ||
|
|
04844b180b | ||
|
|
c21b563614 | ||
|
|
70f0483f97 | ||
|
|
e425de9582 | ||
|
|
093a8be8a3 | ||
|
|
d1aa3fea4f | ||
|
|
a2af9fef2d | ||
|
|
ae5163c813 | ||
|
|
0abd8c4108 | ||
|
|
9fc2e4c3a5 | ||
|
|
5d99f7c3a6 | ||
|
|
cc9a166146 | ||
|
|
f52bfacf0e | ||
|
|
86ad8b7e98 | ||
|
|
837336e90f | ||
|
|
80ff661aeb | ||
|
|
73095a325e | ||
|
|
bb4390e089 | ||
|
|
4b062ca76d | ||
|
|
c0119e6a0d | ||
|
|
9cdb67a661 | ||
|
|
6b11fa26ce | ||
|
|
d4f02402bd | ||
|
|
fca655a48d | ||
|
|
0d97580361 | ||
|
|
e94c7eba83 | ||
|
|
7993e7e232 | ||
|
|
4b43847bd8 | ||
|
|
4729444622 | ||
|
|
a1e6f7bdf4 | ||
|
|
407c150631 | ||
|
|
9f3ebe9956 | ||
|
|
835ef0ffc1 | ||
|
|
a4c0d89d55 | ||
|
|
9c68cff317 | ||
|
|
1e404e379b | ||
|
|
88f6a67802 | ||
|
|
cc887beae9 | ||
|
|
2dc8fba40a | ||
|
|
163782d4cd | ||
|
|
f36f176849 | ||
|
|
3bc0aa8dca | ||
|
|
80dfa85c82 | ||
|
|
aacdf50f63 | ||
|
|
11ad9c8dd9 | ||
|
|
d2305a7900 | ||
|
|
4b0c707fd3 | ||
|
|
69a4f51d58 | ||
|
|
ccb8a1144c | ||
|
|
e0abc972ff | ||
|
|
7a1dfbfb8d | ||
|
|
1abd57567f | ||
|
|
4e7c976608 | ||
|
|
7ad7d1807e | ||
|
|
7d9f973c62 | ||
|
|
e658457b1b | ||
|
|
24609236b5 | ||
|
|
8056d02c0d | ||
|
|
f3b73f40f5 | ||
|
|
5d284b8c27 | ||
|
|
2814a6f680 | ||
|
|
3088535806 | ||
|
|
51cb03a93e | ||
|
|
b09aaabd14 | ||
|
|
1e96f8c3d9 | ||
|
|
e41de7575c | ||
|
|
32b1e0db51 | ||
|
|
381374209a | ||
|
|
ebb4d402b7 | ||
|
|
eaf9cd59e9 | ||
|
|
63977da7ee | ||
|
|
7626a0c121 | ||
|
|
24af94c569 | ||
|
|
de42895096 | ||
|
|
e3ec275a51 | ||
|
|
2202994060 | ||
|
|
44310876d2 | ||
|
|
f10cf58ec3 | ||
|
|
beaad6c49d | ||
|
|
923a741c5a | ||
|
|
3adca4588c | ||
|
|
17600deace | ||
|
|
1aa36b6b2c | ||
|
|
326220762e | ||
|
|
8f8f033d1a | ||
|
|
9a705ed44d | ||
|
|
5d84d6d43f | ||
|
|
bbf7f8d8c3 | ||
|
|
fd76332aca | ||
|
|
78b0b24c25 | ||
|
|
39231f8cf9 | ||
|
|
fb34ecfa7d | ||
|
|
6dd6436291 | ||
|
|
5b7b39a300 | ||
|
|
f672269a8c | ||
|
|
34fd6763e1 | ||
|
|
4fb743e934 | ||
|
|
94d9bb8a18 | ||
|
|
e59f00fcd0 | ||
|
|
078b1ec377 | ||
|
|
f52d28e75f | ||
|
|
9b85a89e71 | ||
|
|
9770f10a8c | ||
|
|
f091774afd | ||
|
|
9c82a842b6 | ||
|
|
590c1467bc | ||
|
|
e1cc6bc624 | ||
|
|
f1d186d6ae | ||
|
|
f7948285f7 | ||
|
|
8d1127182f | ||
|
|
f64c82a8cb | ||
|
|
f12ccfa02a | ||
|
|
a3a9def48d | ||
|
|
df0bdeb8a3 | ||
|
|
4f80bb87c2 | ||
|
|
bf2fa212aa | ||
|
|
28e7934aeb | ||
|
|
b53f185ec6 | ||
|
|
67d12d3120 | ||
|
|
564f5dbf64 | ||
|
|
77a28a2de7 | ||
|
|
edecd8dc22 | ||
|
|
813e9135c6 | ||
|
|
51afc8bdfb | ||
|
|
0dc485d8d7 | ||
|
|
72e524963c | ||
|
|
7723c633a0 | ||
|
|
5870e6367c | ||
|
|
246d9b94b7 | ||
|
|
c17918007d | ||
|
|
33970276b2 | ||
|
|
509f9083c9 | ||
|
|
6f74c565e8 | ||
|
|
794f07c3b0 | ||
|
|
66f518b531 | ||
|
|
c0d05132df | ||
|
|
a5aa20ac5c | ||
|
|
86f209800a | ||
|
|
7c34387d22 | ||
|
|
5e476a2e5a | ||
|
|
1210b4f59a | ||
|
|
6b618a5824 | ||
|
|
6824d23dfe | ||
|
|
bb46d950df | ||
|
|
5472898759 | ||
|
|
d5a1872f93 | ||
|
|
cb4a256d5f | ||
|
|
c0d8337417 | ||
|
|
9d02066615 | ||
|
|
104e6564f7 | ||
|
|
cabe989555 | ||
|
|
1109004727 | ||
|
|
f88a64df1c | ||
|
|
9b897629ba | ||
|
|
6fd01a6721 | ||
|
|
fbd5835bd9 | ||
|
|
95181546b4 | ||
|
|
857803e362 | ||
|
|
98e2564330 | ||
|
|
bd77354926 | ||
|
|
340cfb0104 | ||
|
|
be36b31c4e | ||
|
|
22c38a484f | ||
|
|
7d83f9d644 | ||
|
|
98fc8669e1 | ||
|
|
808319e781 | ||
|
|
2ed9199bf1 | ||
|
|
ab27e0a74d | ||
|
|
53b60c9244 | ||
|
|
613a0f8006 | ||
|
|
b36c24c508 | ||
|
|
74ae43b092 | ||
|
|
1da208ad10 | ||
|
|
25f176875d | ||
|
|
d641fbb635 | ||
|
|
8fdec117cd | ||
|
|
e26e2e44cc | ||
|
|
07ace68d75 | ||
|
|
36fd693728 | ||
|
|
9c260164ef | ||
|
|
5120158bdb | ||
|
|
e75dace600 | ||
|
|
cf86481d31 | ||
|
|
6a5de3e0b6 | ||
|
|
b007acfa95 | ||
|
|
9b9f2fe428 | ||
|
|
26634cdf0b | ||
|
|
bb4693ddfc | ||
|
|
00e64e3518 | ||
|
|
89b2234c17 | ||
|
|
d9745b3477 | ||
|
|
979768fc9f | ||
|
|
70d2ebf55b | ||
|
|
3ce8565513 | ||
|
|
734454ee85 | ||
|
|
ebb9af1eb2 | ||
|
|
8e24dbe22f | ||
|
|
91345e3f9b | ||
|
|
6b5a75ff84 | ||
|
|
5725fbef70 | ||
|
|
fb81627b83 | ||
|
|
aeaf6eee18 | ||
|
|
cfab9221f2 | ||
|
|
90478a04b6 | ||
|
|
a5bc0a61ca | ||
|
|
22c913f48a | ||
|
|
02a6282244 | ||
|
|
c0e3e94e57 | ||
|
|
662bfe0ab8 | ||
|
|
610ec1a6a4 | ||
|
|
c2f7cd6d19 | ||
|
|
c6f0b14e92 | ||
|
|
a8698b6065 | ||
|
|
c627226ac9 | ||
|
|
b2e659bd3f | ||
|
|
fe5d3a1892 | ||
|
|
89dd4be9f1 | ||
|
|
9e02c26908 | ||
|
|
237f5264d9 | ||
|
|
01fac30329 | ||
|
|
71cab7d453 | ||
|
|
d132202662 | ||
|
|
58ec3d912b | ||
|
|
02f57cd69d | ||
|
|
e3a8552774 | ||
|
|
af56db4048 | ||
|
|
2ca2f4e135 | ||
|
|
5296e61d35 | ||
|
|
630954c94e | ||
|
|
70758927cd | ||
|
|
8b23dcd69e | ||
|
|
ed712fb196 | ||
|
|
bdc8202422 | ||
|
|
68cc919aaa | ||
|
|
c6c5ce0619 | ||
|
|
5b4a3805bf | ||
|
|
51e3fc8cab | ||
|
|
a91584853c | ||
|
|
cec8e9188b | ||
|
|
ba239de8be | ||
|
|
1accc36d8c | ||
|
|
bfa4abbf87 | ||
|
|
9a488d1d8c | ||
|
|
7da5f3a04d | ||
|
|
a44e9f2c09 | ||
|
|
0cd1b232ed | ||
|
|
3ded17badf | ||
|
|
6d6a9cc72b | ||
|
|
4d9c37f2db | ||
|
|
45c46c1794 | ||
|
|
4be3f23757 | ||
|
|
d2bf8b25de | ||
|
|
62200eda9e | ||
|
|
e8a2e30bb5 | ||
|
|
48b2fb0b36 | ||
|
|
cc9dad6bb8 | ||
|
|
2a7b646c12 | ||
|
|
3c8acac758 | ||
|
|
737ebe408c | ||
|
|
c66a1991d8 | ||
|
|
792d657a4f | ||
|
|
560bd97fcd | ||
|
|
c1c325505b | ||
|
|
85f39ec0ef | ||
|
|
02074a93ef | ||
|
|
7717897357 | ||
|
|
3d25ef1be5 | ||
|
|
d688187e23 | ||
|
|
74a159ab99 | ||
|
|
3c7a7f0fa4 | ||
|
|
a1f9230169 | ||
|
|
be00b64b7e | ||
|
|
fc119ecd98 | ||
|
|
1abe9d4c01 | ||
|
|
9693c6e941 | ||
|
|
4521907b83 | ||
|
|
83c1129ed5 | ||
|
|
04a0216ebc | ||
|
|
930120fc4d | ||
|
|
160433ec9c | ||
|
|
693e18e5d9 | ||
|
|
b8d07cf43f | ||
|
|
cb87c248d5 | ||
|
|
8d4191b1a0 | ||
|
|
09b001e891 | ||
|
|
bdcacf0cfb | ||
|
|
d86e4ac0b6 | ||
|
|
d9104ceced | ||
|
|
7af92b3b30 | ||
|
|
1af95fcdaa | ||
|
|
f1a3af6fce | ||
|
|
26fad5e8fe | ||
|
|
23c3135051 | ||
|
|
d2b9102f84 | ||
|
|
5c6cb2a422 | ||
|
|
8a2f8ed6ee | ||
|
|
c104860448 | ||
|
|
5799bf2d62 | ||
|
|
f278517276 | ||
|
|
90cfac9b52 | ||
|
|
76f86c74ad | ||
|
|
1d7c25b561 | ||
|
|
bfe73ed9b4 | ||
|
|
856a53c33c | ||
|
|
770c08725f | ||
|
|
1234cc8152 | ||
|
|
47129afcfa | ||
|
|
072755ce7d | ||
|
|
0226478c63 | ||
|
|
3e17d9398b | ||
|
|
c18a386e09 | ||
|
|
057feaf5bd | ||
|
|
6fd7a841bd | ||
|
|
e86aab5e88 | ||
|
|
a5e6c04cb0 | ||
|
|
b5f977e46d | ||
|
|
0f3efde288 | ||
|
|
0d4c0d36af | ||
|
|
f4bbcc9fa3 | ||
|
|
6a4fee641d | ||
|
|
2d8abac0e3 | ||
|
|
f8721329ba | ||
|
|
13f915cc77 | ||
|
|
f0827fef38 | ||
|
|
cd9efec4d2 | ||
|
|
1753dbe8b0 | ||
|
|
a0591730b1 | ||
|
|
d05e1fdfb3 | ||
|
|
b5f5aff626 | ||
|
|
63371ce76e | ||
|
|
651dc651f5 | ||
|
|
c5993ba8b9 | ||
|
|
e666ae2b2e | ||
|
|
5647f957a7 | ||
|
|
73f675aebe | ||
|
|
cd5f3f2f2b | ||
|
|
b4e944e9ba | ||
|
|
741824ef87 | ||
|
|
15fbe86124 | ||
|
|
42d78f9a68 | ||
|
|
9fe28e830a | ||
|
|
4f218ea6d3 | ||
|
|
531247deb4 | ||
|
|
ac827f6861 | ||
|
|
e3cca44dce | ||
|
|
9002bbe77e | ||
|
|
6fc98eb1cb | ||
|
|
c566f1e7c9 | ||
|
|
5a15d29a22 | ||
|
|
d31c23d25a | ||
|
|
cb369dd326 | ||
|
|
91f1e89201 | ||
|
|
c7d6d38ab7 | ||
|
|
5424b85685 | ||
|
|
391d1127ca | ||
|
|
f9b8a0a661 | ||
|
|
25e893f712 | ||
|
|
8d83b6ada3 | ||
|
|
8aa7486e71 | ||
|
|
ebf3d813cb | ||
|
|
16fe9e241a | ||
|
|
b96904ec75 | ||
|
|
0b813ed159 | ||
|
|
4faa903fb3 | ||
|
|
2215e4db49 | ||
|
|
42e0895dad | ||
|
|
d2519c9a5b | ||
|
|
c6fc2a98ae | ||
|
|
a079284729 | ||
|
|
f0d4f43dba | ||
|
|
9e2e883c82 | ||
|
|
d9c7bbd3d2 | ||
|
|
96c822ffb3 | ||
|
|
a261dcd171 | ||
|
|
5db5f7217f | ||
|
|
f87d2ab89e | ||
|
|
6882ffaa00 | ||
|
|
603cb1db34 | ||
|
|
fe2fe1e08a | ||
|
|
97117f2fde | ||
|
|
27e54fae4c | ||
|
|
f1887449ab | ||
|
|
bf3c15708d | ||
|
|
d4da9d823b | ||
|
|
092e682591 | ||
|
|
af0c111d2e | ||
|
|
fb091f1b48 | ||
|
|
fe88ea4f9a | ||
|
|
a937cde7a3 | ||
|
|
c4d453b738 | ||
|
|
e17409e0b1 | ||
|
|
01106d801a | ||
|
|
db5d737235 | ||
|
|
82a240998b | ||
|
|
08ba652464 | ||
|
|
6a6c7d4b90 | ||
|
|
21a3037bdf | ||
|
|
59477a9fa9 | ||
|
|
dae148af76 | ||
|
|
b50863ff9f | ||
|
|
a61fd030ff | ||
|
|
cf369a9f12 | ||
|
|
3611f9df94 | ||
|
|
eb9ca5d7e3 | ||
|
|
bce998cb53 | ||
|
|
f26f2d9e9b | ||
|
|
99c1f14581 | ||
|
|
410597eca7 | ||
|
|
216486a4c6 | ||
|
|
8cae6bb627 | ||
|
|
ebac3f5547 | ||
|
|
c2b016cb5f | ||
|
|
7e470d2cb5 | ||
|
|
fa61dada55 | ||
|
|
fb0168ea50 | ||
|
|
1ddd34059b | ||
|
|
7affd0579e | ||
|
|
4bced29ec9 | ||
|
|
82e39ccf52 | ||
|
|
4ac204d230 | ||
|
|
9eb1c3a731 | ||
|
|
6e0b7cc650 | ||
|
|
2d7a2bb57f | ||
|
|
8a79748b6d | ||
|
|
75595dd07f | ||
|
|
bf050ece0f | ||
|
|
62ef532801 | ||
|
|
43660d9c8f | ||
|
|
f4c5b4c11d | ||
|
|
89f164bdd7 | ||
|
|
e2cf49aefd | ||
|
|
cb7bafbdc0 | ||
|
|
8317b167ce | ||
|
|
4b78169152 | ||
|
|
69a79f660f | ||
|
|
20f621d05c | ||
|
|
641740db77 | ||
|
|
686c42ae07 | ||
|
|
ac11617655 | ||
|
|
d7d3d6c808 | ||
|
|
a6a9ad8cc0 | ||
|
|
84a9dcf6cb | ||
|
|
01823d18c6 | ||
|
|
98330cdfa1 | ||
|
|
ff794dc215 | ||
|
|
1b1ba985a9 | ||
|
|
178414cef4 | ||
|
|
6ea4098679 | ||
|
|
e9ef94e8af | ||
|
|
c8196c41d4 | ||
|
|
ec39ff267c | ||
|
|
9818cfb572 | ||
|
|
f4c1a5ea35 | ||
|
|
e93c3483c8 | ||
|
|
6c653d8f2a | ||
|
|
93466d9a66 | ||
|
|
999962d161 | ||
|
|
9cd10686bb | ||
|
|
6fdf578919 | ||
|
|
dab0a8ec27 | ||
|
|
3f15035c1a | ||
|
|
1dc9f5e842 | ||
|
|
c92c2d7b75 | ||
|
|
6c21723ecb | ||
|
|
c96f1e76e2 | ||
|
|
78d02f50af | ||
|
|
0477551293 | ||
|
|
e01560ab89 | ||
|
|
5e4a422495 | ||
|
|
e8af744c97 | ||
|
|
08609af053 | ||
|
|
bed6eb70af | ||
|
|
62b1eba836 | ||
|
|
c6223fd6b0 | ||
|
|
73817a311b | ||
|
|
0d62f30d7b | ||
|
|
34794d2554 | ||
|
|
a0e056abc1 | ||
|
|
8ced769fae | ||
|
|
54b07a9446 | ||
|
|
d9f0ece812 | ||
|
|
8e49b8365c | ||
|
|
2650706be1 | ||
|
|
f62ad19ecd | ||
|
|
1f0b55bfed | ||
|
|
ed9d331f89 | ||
|
|
7b9fb32fae | ||
|
|
a0add9ec7a | ||
|
|
67fa3f728c | ||
|
|
55943f8a7d | ||
|
|
dbb8e5421a | ||
|
|
9872c019b2 | ||
|
|
b28d10ad65 | ||
|
|
1ab86c9331 | ||
|
|
a9cfb06ee9 | ||
|
|
561e7b43e1 | ||
|
|
4ba641b13f | ||
|
|
4de0fd71e1 | ||
|
|
452a8512c4 | ||
|
|
31897bc7b5 | ||
|
|
28db040e57 | ||
|
|
9d9935a625 | ||
|
|
ce38e74ba2 | ||
|
|
4520114780 | ||
|
|
9a182d615a | ||
|
|
fda6ba7c8c | ||
|
|
6b2b9d0fde | ||
|
|
b6ddf2a451 | ||
|
|
0331776614 | ||
|
|
2df3bc813a | ||
|
|
ce7d98ff1f | ||
|
|
51a6db995a | ||
|
|
66fcbee0df | ||
|
|
3df0ba27fd | ||
|
|
37835380e3 | ||
|
|
6db2ce2a40 | ||
|
|
bf9533007b | ||
|
|
651f73ed71 | ||
|
|
8c825d477f | ||
|
|
13474985dc | ||
|
|
468692f5e4 | ||
|
|
cc93113934 | ||
|
|
1d38b2ce6a | ||
|
|
d6760bfbee | ||
|
|
45ad0f6bdf | ||
|
|
d0ec102c53 | ||
|
|
39d2f6b981 | ||
|
|
feadcb24df | ||
|
|
5511a93e9c | ||
|
|
a587547e2c | ||
|
|
ba65b5a58f | ||
|
|
1eb7b4724c | ||
|
|
a4588cc848 | ||
|
|
e3fee55e3d | ||
|
|
7b95c46cf5 | ||
|
|
bd550f8a1e | ||
|
|
fcc4ed396a | ||
|
|
ba40ac4e90 | ||
|
|
15b5df10a4 | ||
|
|
7e3c6b4944 | ||
|
|
e922c9bc72 | ||
|
|
1382fa8901 | ||
|
|
a0df8e7989 | ||
|
|
13bcb062b2 | ||
|
|
977932250d | ||
|
|
f555568d50 | ||
|
|
9e183f8994 | ||
|
|
7ea8c3d173 | ||
|
|
467d8b2cd2 | ||
|
|
e3a3ca4c0d | ||
|
|
b40826f4ee | ||
|
|
62e1f8e96d | ||
|
|
04eeb28c62 | ||
|
|
85256f545c | ||
|
|
e9bbcc9135 | ||
|
|
c823ca7ac6 | ||
|
|
f85497a421 | ||
|
|
1e95279fc0 | ||
|
|
d92328dbb4 | ||
|
|
a4eafa5c66 | ||
|
|
11ea71f7b9 | ||
|
|
da16c0159b | ||
|
|
723c6f5436 | ||
|
|
f5514c4fea | ||
|
|
3c808ca895 | ||
|
|
96c6c0ff00 | ||
|
|
ae80607248 | ||
|
|
d09bf306ce | ||
|
|
0bf39bd152 | ||
|
|
86e8470784 | ||
|
|
4da9273b28 | ||
|
|
e5dceddfe7 | ||
|
|
8d99860c55 | ||
|
|
8d253ed5ff | ||
|
|
1c619fd4bc | ||
|
|
3f51595d25 | ||
|
|
4bc645074a | ||
|
|
ef3496d55b | ||
|
|
dbccf39a9f | ||
|
|
ba0d60aa35 | ||
|
|
9e2cc6bb21 | ||
|
|
73bc2036e3 | ||
|
|
d96ce25b1c | ||
|
|
0eb14052ec | ||
|
|
e5f07dcae8 | ||
|
|
0df77a5a65 | ||
|
|
5b73561aba | ||
|
|
3cc23c8e5b | ||
|
|
95fc47048a | ||
|
|
16312c921d | ||
|
|
b58c04c28b | ||
|
|
901170ab41 | ||
|
|
3f09575740 | ||
|
|
4241cbc3a0 | ||
|
|
966765dac7 | ||
|
|
16ed2c1572 | ||
|
|
0a926337cb | ||
|
|
f214ead6f6 | ||
|
|
61cba414bf | ||
|
|
509057f84c | ||
|
|
64050337cf | ||
|
|
8d37a0a39e | ||
|
|
e9bacfe58a | ||
|
|
02907f12f1 | ||
|
|
9f45bce55f | ||
|
|
80258efc7f | ||
|
|
e28ebcb564 | ||
|
|
40022decb4 | ||
|
|
d121ebe078 | ||
|
|
70a7446ad4 | ||
|
|
b168dde502 | ||
|
|
c6c4fbab99 | ||
|
|
ca2e7a9814 | ||
|
|
5d63190d63 | ||
|
|
5bb0698ded | ||
|
|
0653d99e4d | ||
|
|
4ab7308e77 | ||
|
|
6b089d35a9 | ||
|
|
6b1b8bb522 | ||
|
|
ea68810f58 | ||
|
|
ddc85579c9 | ||
|
|
1c90edf206 | ||
|
|
51fa558325 | ||
|
|
de5ccf9c47 | ||
|
|
1a4375ed73 | ||
|
|
3a963fab0b | ||
|
|
3623d41fd3 | ||
|
|
5f773c30cb | ||
|
|
ac184a6fe3 | ||
|
|
4d729b9d39 | ||
|
|
dea9aef965 | ||
|
|
6c76f94a1a | ||
|
|
928b5a04ca | ||
|
|
15427e0aa1 | ||
|
|
183a18f618 | ||
|
|
169fb4d6f4 | ||
|
|
9035db1137 | ||
|
|
2630aa6ac8 | ||
|
|
3d5c8b0053 | ||
|
|
1ca9e7ab3e | ||
|
|
f41acc2990 | ||
|
|
a9612fc5cc | ||
|
|
310409355a | ||
|
|
8eef613d4b | ||
|
|
0c38f1db08 | ||
|
|
f1e8753f16 | ||
|
|
74a53af316 | ||
|
|
56856309c5 | ||
|
|
4f720c30dd | ||
|
|
58d8b7e148 | ||
|
|
35c546782f | ||
|
|
9333efd3c3 | ||
|
|
88064cdbaf | ||
|
|
2b8e992646 | ||
|
|
41dfd84543 | ||
|
|
73f3d5dde8 | ||
|
|
9461a70784 | ||
|
|
13199a3013 | ||
|
|
b6cc0471c5 | ||
|
|
0555912a13 | ||
|
|
ada00948c5 | ||
|
|
0c7caf80b3 | ||
|
|
6a490ab53b | ||
|
|
f9d62599a4 | ||
|
|
dbd941f464 | ||
|
|
ff8e730b8a | ||
|
|
9fe6c0a3c0 | ||
|
|
e405e35736 | ||
|
|
af167a58a8 | ||
|
|
eed9999acc | ||
|
|
bc4ec9652c | ||
|
|
69feb7138a | ||
|
|
a97ef5d1e5 | ||
|
|
8d46af0dc2 | ||
|
|
126d82c4ce | ||
|
|
e8763a6380 | ||
|
|
5abb561f91 | ||
|
|
22b4e8ed83 | ||
|
|
641c4f1bde | ||
|
|
90c615075e | ||
|
|
2baeac04d1 | ||
|
|
fb5805a43c | ||
|
|
6a333e79d2 | ||
|
|
9ad9621ad6 | ||
|
|
ca3ce816a3 | ||
|
|
cbccaf9527 | ||
|
|
ebc971f8dc | ||
|
|
d0a138e718 | ||
|
|
8b0b5f4026 | ||
|
|
42f0bddd60 | ||
|
|
88a77127ea | ||
|
|
57518c6950 | ||
|
|
79b3cef729 | ||
|
|
f05bd8b0f6 | ||
|
|
0076cdccc9 | ||
|
|
e1df9faad7 | ||
|
|
bb51393332 | ||
|
|
52f5d7d346 | ||
|
|
a874a25b69 | ||
|
|
efbde82ca2 | ||
|
|
4f64c72d0d | ||
|
|
bded3da5df | ||
|
|
195e276e4b | ||
|
|
39e6607afd | ||
|
|
1b74473421 | ||
|
|
97250d8193 | ||
|
|
0816dd15f9 | ||
|
|
427c13a104 | ||
|
|
5604a6837f | ||
|
|
5f0032bb7f | ||
|
|
2c2a570819 | ||
|
|
082967095e | ||
|
|
6662fa7b7b | ||
|
|
662800cc67 | ||
|
|
392775b9ab | ||
|
|
8d4817b710 | ||
|
|
3e534404c3 | ||
|
|
0fbfabe5d3 | ||
|
|
30054d177b | ||
|
|
e17ab5f455 | ||
|
|
c94871b5ef | ||
|
|
9a8ef2a30d | ||
|
|
45ac40cc85 | ||
|
|
76b65c5dea | ||
|
|
7ab26e5b0d | ||
|
|
d93ac31b4e | ||
|
|
031c8a5594 | ||
|
|
5793a9c376 | ||
|
|
752117e07a | ||
|
|
d476c8bbd8 | ||
|
|
a97df22da4 | ||
|
|
8360fccd06 | ||
|
|
fc39281bdb | ||
|
|
2a9983032d | ||
|
|
f676a0bf9e | ||
|
|
313fd5582a | ||
|
|
f63c1c95fe | ||
|
|
f384a3352b | ||
|
|
7e08575245 | ||
|
|
40b2bd2595 | ||
|
|
68616957ab | ||
|
|
d96be650e4 | ||
|
|
122a59c723 | ||
|
|
3ee7ccff48 | ||
|
|
9f5fec7135 | ||
|
|
648b6f2038 | ||
|
|
163a013e1f | ||
|
|
2652fbc1cb | ||
|
|
2760f40a7c | ||
|
|
abc6f7a29f | ||
|
|
9d1bd61720 | ||
|
|
5d7e7a48ad | ||
|
|
bddd477bd7 | ||
|
|
f53b5121af | ||
|
|
4befae3a8d | ||
|
|
1616f15a40 | ||
|
|
aae7ec6146 | ||
|
|
1c5e84324b | ||
|
|
428adcf47f | ||
|
|
59627ea9ef | ||
|
|
020bb160b9 | ||
|
|
4016a37573 | ||
|
|
817524de4b | ||
|
|
43ec31e393 | ||
|
|
ca3da4adf5 | ||
|
|
4adc0eb563 | ||
|
|
a54151fb86 | ||
|
|
5d70a4749a | ||
|
|
2d8ddfedfa | ||
|
|
16c1b0bc2a | ||
|
|
0c46022f1b | ||
|
|
6d85772bd1 | ||
|
|
c20d05ba45 | ||
|
|
dea874e36e | ||
|
|
8fa227f3da | ||
|
|
76921ced71 | ||
|
|
a17496d291 | ||
|
|
cb2fc93f78 | ||
|
|
3666d0ab73 | ||
|
|
b45256c31b | ||
|
|
bc33356a57 | ||
|
|
9968bebed2 | ||
|
|
43e9f9998f | ||
|
|
eeca43014a | ||
|
|
b5b5abdc82 | ||
|
|
5eab0d47e4 | ||
|
|
0249966741 | ||
|
|
6437ba8c6a | ||
|
|
b2b2534dcc | ||
|
|
c06957b2f0 | ||
|
|
00b22d50c7 | ||
|
|
a0cfc2110a | ||
|
|
0615c6624e | ||
|
|
d2bf2f8f5a | ||
|
|
7a486e1158 | ||
|
|
8e936dc3c2 | ||
|
|
10ca7de6ba | ||
|
|
e0f2d59864 | ||
|
|
05f8352837 | ||
|
|
79dc3ab3fa | ||
|
|
29436c1e5f | ||
|
|
d13afe0b2e | ||
|
|
afd40cae77 | ||
|
|
8a0f2010a2 | ||
|
|
f8c6050abe | ||
|
|
dea98d5e87 | ||
|
|
f8e85e3208 | ||
|
|
b01f4ed802 | ||
|
|
d7244bd9ba | ||
|
|
039ad5727f | ||
|
|
6d55083ef6 | ||
|
|
a02800695a | ||
|
|
6683e5f8ec | ||
|
|
8d2ec59f71 | ||
|
|
c8e0bb04b6 | ||
|
|
4d9bacbecc | ||
|
|
326992f3ba | ||
|
|
9bdd1087ba | ||
|
|
806a67b1b0 | ||
|
|
fdfda47f0c | ||
|
|
2065a0b349 | ||
|
|
2272ff5ed7 | ||
|
|
92a43a5930 | ||
|
|
b81978c67c | ||
|
|
5d3c5a151f | ||
|
|
937240f244 | ||
|
|
ee4aff0ca4 | ||
|
|
4422613248 | ||
|
|
4abd4a6882 | ||
|
|
ae4cb685c0 | ||
|
|
acba505144 | ||
|
|
858dab7814 | ||
|
|
e034b4a331 | ||
|
|
2630d0b56c | ||
|
|
9f364bb9dd | ||
|
|
c1b9a9477e | ||
|
|
203b3058a2 | ||
|
|
a9a91603d1 | ||
|
|
27333035c0 | ||
|
|
0c2c030b71 | ||
|
|
56a21b3968 | ||
|
|
c5b0a40279 | ||
|
|
716c62ba8f | ||
|
|
e4144f2c64 | ||
|
|
ee6abd45db | ||
|
|
804c2dc3ae | ||
|
|
79cbdb537e | ||
|
|
903ca342cb | ||
|
|
8628105eef | ||
|
|
f4d74c4935 | ||
|
|
9486a6b2a3 | ||
|
|
10884f2371 | ||
|
|
14dda66c03 | ||
|
|
c6553aef3d | ||
|
|
65011d1af7 | ||
|
|
7b429bdcd7 | ||
|
|
9623974ed5 | ||
|
|
7b5a35cbc6 | ||
|
|
e26dc9a2b7 | ||
|
|
d5e8b23577 | ||
|
|
69a036b2b7 | ||
|
|
af518cf1f4 | ||
|
|
73a19614e1 | ||
|
|
bafc8f829a | ||
|
|
0d5550b043 | ||
|
|
6c6f7fb3bb | ||
|
|
cff5bab403 | ||
|
|
6e8b11113e | ||
|
|
9d973d9252 | ||
|
|
a0c43f1fe3 | ||
|
|
d405f74c54 | ||
|
|
ab06570a50 | ||
|
|
3ab4dcb7c1 | ||
|
|
8d5dd605d8 | ||
|
|
6d46332f96 | ||
|
|
e07e5b5cf8 | ||
|
|
d877795561 | ||
|
|
5ae2546339 | ||
|
|
4acd56e661 | ||
|
|
899869b51b | ||
|
|
f8d3bd7da3 | ||
|
|
373295b498 | ||
|
|
ac90f8fddc | ||
|
|
1c1a635d5a | ||
|
|
1c1442a4fc | ||
|
|
477bc95a00 | ||
|
|
02f45abcb9 | ||
|
|
46d4525c45 | ||
|
|
abdb27ea29 | ||
|
|
4ffca32653 | ||
|
|
e81ea8126a | ||
|
|
2a263b0e82 | ||
|
|
1a2392274a | ||
|
|
7a58814c61 | ||
|
|
3e1a92f486 | ||
|
|
5668ad01d5 | ||
|
|
056e0d79ed | ||
|
|
defd527625 | ||
|
|
ec1da1f540 | ||
|
|
ed71b9f31d | ||
|
|
1b9f6ba58e | ||
|
|
08704e8c93 | ||
|
|
54bd8507bc | ||
|
|
c72f8f6499 | ||
|
|
2bc6587e84 | ||
|
|
ee68732419 | ||
|
|
96744997e8 | ||
|
|
95181b0a7f | ||
|
|
d5a0b52eef | ||
|
|
21df4861b4 | ||
|
|
0b5fbadf66 | ||
|
|
d509efa43e | ||
|
|
cfaf3efa85 | ||
|
|
a11a9d42f0 | ||
|
|
a389164cea | ||
|
|
3d7e33de85 | ||
|
|
9918e3390e | ||
|
|
7028bc1415 | ||
|
|
f387ce08bc | ||
|
|
7c85d5fc3f | ||
|
|
79936b88d0 | ||
|
|
71c8c8a72e | ||
|
|
25aca3c4d3 | ||
|
|
01016b0444 | ||
|
|
6a90c7c653 | ||
|
|
33850dcfff | ||
|
|
8ee78137e6 | ||
|
|
9fdaa92bd1 | ||
|
|
c5dde7c2f5 | ||
|
|
d02bb73fe8 | ||
|
|
cdace54b7a | ||
|
|
58bec7b034 | ||
|
|
97ec147e0b | ||
|
|
bae6954b54 | ||
|
|
66718634c9 | ||
|
|
cb381bd3a3 | ||
|
|
f6c7fdb8ed | ||
|
|
7970b2a3ee | ||
|
|
f438d4695d | ||
|
|
e83cf382fd | ||
|
|
d8f0e5eb17 | ||
|
|
11420fa84f | ||
|
|
efa05a4d50 | ||
|
|
4126124167 | ||
|
|
c6e7fb6ac8 | ||
|
|
8de76205f9 | ||
|
|
11a0cc5658 | ||
|
|
1fb0d8bf80 | ||
|
|
5a2d6c0b3e | ||
|
|
ffd7463450 | ||
|
|
348ea55589 | ||
|
|
93bb2022a7 | ||
|
|
0571b79290 | ||
|
|
8a33de806c | ||
|
|
c766e16b5b | ||
|
|
904baec1b9 | ||
|
|
bc16861930 | ||
|
|
8073e1cb9a | ||
|
|
60ad401f0e | ||
|
|
b5e599fac5 | ||
|
|
6c186447d7 | ||
|
|
6de09c5950 | ||
|
|
4038abbe10 | ||
|
|
0c192e5a3e | ||
|
|
def1c35377 | ||
|
|
ff219cf91a | ||
|
|
c0649f7385 | ||
|
|
c3c6d00a9c | ||
|
|
30557bbe4e | ||
|
|
2e92ad9e51 | ||
|
|
9b387910ba | ||
|
|
4d2101b092 | ||
|
|
d945cb2a72 | ||
|
|
d36e5b0167 | ||
|
|
f240a11c54 | ||
|
|
b4eb4ee97d | ||
|
|
79a6556b0d | ||
|
|
53ad2a5114 | ||
|
|
3cdeeac964 | ||
|
|
cb2e1a0b1e | ||
|
|
fa0af50d02 | ||
|
|
f24649295d | ||
|
|
7397f54010 | ||
|
|
129653a6d6 | ||
|
|
9ceef4c096 | ||
|
|
2c328c744d | ||
|
|
ababeef84d | ||
|
|
77941bcf73 | ||
|
|
016d6939d5 | ||
|
|
5c12350248 | ||
|
|
5a10a9494f | ||
|
|
4593fc9ef2 | ||
|
|
ef1daf8f1c | ||
|
|
0f0654d49e | ||
|
|
ad85f21faa | ||
|
|
4895efd4ca | ||
|
|
f25d653ca2 | ||
|
|
06d145777e | ||
|
|
600eb0d0d0 | ||
|
|
43f834c0bd | ||
|
|
4a2dac9d46 | ||
|
|
2750e60768 | ||
|
|
aec4870b51 | ||
|
|
80041db81b | ||
|
|
0166681dde | ||
|
|
45941e52da | ||
|
|
dfbb9b2b3b | ||
|
|
f571979259 | ||
|
|
9ceeb3411b | ||
|
|
b303f20cac | ||
|
|
cf7d10046c | ||
|
|
c13ab53b45 | ||
|
|
12cd0ee839 | ||
|
|
4a1a150f84 | ||
|
|
c19a2484c3 | ||
|
|
3f1b906de0 | ||
|
|
c8ec2c7375 | ||
|
|
e169d3b019 | ||
|
|
bab788111a | ||
|
|
8e3ed52924 | ||
|
|
1a034d45f6 | ||
|
|
69f1798758 | ||
|
|
fdab2e28e9 | ||
|
|
d26baf1f57 | ||
|
|
3f8ab486c2 | ||
|
|
020e08143e | ||
|
|
addfae2f46 | ||
|
|
0fea75a345 | ||
|
|
a16aaa8a48 | ||
|
|
eddc33c4db | ||
|
|
a37f84da01 | ||
|
|
a126670f72 | ||
|
|
5729798aaf | ||
|
|
43a6c51ab8 | ||
|
|
9c68e68125 | ||
|
|
148d46a2b1 | ||
|
|
50114a4173 | ||
|
|
fbc86e29d4 | ||
|
|
fe824fd733 | ||
|
|
b1d6decbb2 | ||
|
|
6931116561 | ||
|
|
336127da55 | ||
|
|
604c7e4992 | ||
|
|
72278b4f0f | ||
|
|
f069719ef3 | ||
|
|
a049b13741 | ||
|
|
7e3040076b | ||
|
|
8f14f5aa21 | ||
|
|
e89a367131 | ||
|
|
9aead96518 | ||
|
|
4bca6c3696 | ||
|
|
dc1daca739 | ||
|
|
6fd2113870 | ||
|
|
5337a721b0 | ||
|
|
c7087e7ac0 | ||
|
|
8e3aef135d | ||
|
|
dcc7c18ab9 | ||
|
|
b682ffdfa3 | ||
|
|
8623302cc3 | ||
|
|
3ca63d5b6a | ||
|
|
8b62cda91c | ||
|
|
018d63f250 | ||
|
|
9d7b6927d6 | ||
|
|
41ab727c78 | ||
|
|
9d3253dc0c | ||
|
|
eedd7c382b | ||
|
|
833a7e8465 | ||
|
|
050ce467f8 | ||
|
|
5c1ffdf4a8 | ||
|
|
bfe632b543 | ||
|
|
bd9cb92035 | ||
|
|
9e11941aae | ||
|
|
a4755c9851 | ||
|
|
ae7a93b8dc | ||
|
|
1694f859c5 | ||
|
|
84ed87d7c3 | ||
|
|
12f49f66be | ||
|
|
21d4a8c14c | ||
|
|
722a3955c4 | ||
|
|
79205cdccc | ||
|
|
786abc5672 | ||
|
|
933babe9d9 | ||
|
|
fc36e6c2d0 | ||
|
|
467246cc73 | ||
|
|
67403cf95a | ||
|
|
9eb016d0f4 | ||
|
|
61cb9b2713 | ||
|
|
1bbfe9dbf7 | ||
|
|
6d91d57d56 | ||
|
|
d9a88515bc | ||
|
|
a5836ecc60 | ||
|
|
718f4f1b5d | ||
|
|
ef6768433c | ||
|
|
5fd7238ad1 | ||
|
|
a8319407fc | ||
|
|
87f79509a0 | ||
|
|
db35fc92d5 | ||
|
|
b069b7c1bb | ||
|
|
6cc6928ce9 | ||
|
|
c2ce37e6f3 | ||
|
|
9dab76f357 | ||
|
|
5067a4640b | ||
|
|
bfe3cdecc5 | ||
|
|
dab6f97a31 | ||
|
|
d97698f2f8 | ||
|
|
9d7db047e6 | ||
|
|
e553ba2d07 | ||
|
|
6b62af4102 | ||
|
|
95d02ed133 | ||
|
|
b69bb9f149 | ||
|
|
43ed2591d0 | ||
|
|
52757ef89f | ||
|
|
0bade37a26 | ||
|
|
8158470656 | ||
|
|
0b3b26e5ae | ||
|
|
ef15ea8318 | ||
|
|
7cb19a725f | ||
|
|
ca455b380e | ||
|
|
7b60a36eea | ||
|
|
59940e81a8 | ||
|
|
d1505917a3 | ||
|
|
e6edcb5459 | ||
|
|
e7f328c2fe | ||
|
|
48177e8d03 | ||
|
|
17360cd7bd | ||
|
|
a54aa6651c | ||
|
|
d92f06bcfa | ||
|
|
ba5ac424e4 | ||
|
|
43f6fda8b6 | ||
|
|
1047fe72c3 | ||
|
|
0f33dde04e | ||
|
|
a7a6afeb7b | ||
|
|
511898afc9 | ||
|
|
aa86c26d18 | ||
|
|
7a6a9f159b | ||
|
|
463cd80005 | ||
|
|
0023af43d8 | ||
|
|
a66eac1e8c | ||
|
|
d0ad879f4a | ||
|
|
1ae0c7d895 | ||
|
|
628ed13a9e | ||
|
|
11ad99cdcd | ||
|
|
af817ec616 | ||
|
|
38891b76f3 | ||
|
|
400f5c4b48 | ||
|
|
3c3261b7f1 | ||
|
|
6dd41b52c3 | ||
|
|
f15c2733eb | ||
|
|
0dd6c25b49 | ||
|
|
69c6798aa9 | ||
|
|
50e50fa94b | ||
|
|
2bb106b449 | ||
|
|
fbfb871edf | ||
|
|
44dc9346c2 | ||
|
|
62d2dfd915 | ||
|
|
3aa49f773a | ||
|
|
52a857274d | ||
|
|
32ed46c899 | ||
|
|
4b3a90bb54 | ||
|
|
7c8b5989e8 | ||
|
|
2438fd3666 | ||
|
|
f637114a1a | ||
|
|
ddc6adedbc | ||
|
|
e9beba4122 | ||
|
|
c36504e678 | ||
|
|
887f621f78 | ||
|
|
c89b62a7b4 | ||
|
|
62a9d23da4 | ||
|
|
93ec03cdb9 | ||
|
|
5a4a9e0739 | ||
|
|
e5b0cbd308 | ||
|
|
16dffdb0bf | ||
|
|
90f451d3ca | ||
|
|
0e32cf02cb | ||
|
|
23717539c4 | ||
|
|
cf3de6d7ef | ||
|
|
87d85d14d4 | ||
|
|
f14c8ad131 | ||
|
|
ef1133913d | ||
|
|
ee6a6dff50 | ||
|
|
3c627fdd53 | ||
|
|
62ec121a5e | ||
|
|
11909a25cb | ||
|
|
6c6261ac23 | ||
|
|
c6426d29e2 | ||
|
|
2c89d063c1 | ||
|
|
0328bec61f | ||
|
|
062ecddc24 | ||
|
|
0aaee54a63 | ||
|
|
6d7580b9d0 | ||
|
|
ddbe1edf6d | ||
|
|
baa2ce9256 | ||
|
|
393b121b08 | ||
|
|
d06af4ff72 | ||
|
|
d21f99d1bf | ||
|
|
44e3851f5e | ||
|
|
bbdf70ee90 | ||
|
|
217f732b11 | ||
|
|
6758e78cf1 | ||
|
|
797bdda405 | ||
|
|
df36580657 | ||
|
|
fce6f152e8 | ||
|
|
adb79872b3 | ||
|
|
536363e41e | ||
|
|
af19787ed9 | ||
|
|
ccb53ff1d0 | ||
|
|
74cd77e2f1 | ||
|
|
b56fb8f6f4 | ||
|
|
efe39e6e1a | ||
|
|
0d1b1ddef6 | ||
|
|
1eb91bc91e | ||
|
|
3ee68aba81 | ||
|
|
21d5309644 | ||
|
|
0556990b66 | ||
|
|
13d428c30b | ||
|
|
0bcfee0449 | ||
|
|
cdff6cc2ec | ||
|
|
882f149f6b | ||
|
|
039d9f0ead | ||
|
|
21ee8282a5 | ||
|
|
e9144fa2fc | ||
|
|
a5e76d5c55 | ||
|
|
697bb569ef | ||
|
|
0aaa5779ab | ||
|
|
476a73d1b1 | ||
|
|
f0cd4437d8 | ||
|
|
861023abcf | ||
|
|
33ef75865a | ||
|
|
a1c5ace4ed | ||
|
|
64ee483665 | ||
|
|
1f91e5328a | ||
|
|
8d465c67c2 | ||
|
|
286f174f34 | ||
|
|
ac13c8e79f | ||
|
|
d39165889e | ||
|
|
47a3541787 | ||
|
|
0cab9c0082 | ||
|
|
05152da195 | ||
|
|
6f76fb3613 | ||
|
|
e142d62a26 | ||
|
|
d0bb2a8937 | ||
|
|
48191995b3 | ||
|
|
43fadd6b40 | ||
|
|
9a8fc49f2e | ||
|
|
5a0d0e5f02 | ||
|
|
37438a51b2 | ||
|
|
001e69ab80 | ||
|
|
bdaf9bcdb8 | ||
|
|
68264a0fe0 | ||
|
|
e2c8cfea18 | ||
|
|
adab1463f7 | ||
|
|
73a00438ae | ||
|
|
1caa23e621 | ||
|
|
d15cecb773 | ||
|
|
d7b0d84997 | ||
|
|
0bf3ededf9 | ||
|
|
9f1b4c41a3 | ||
|
|
b040bc9856 | ||
|
|
7b1bcf7033 | ||
|
|
ae9bed4ab7 | ||
|
|
2a13cc33ac | ||
|
|
da533571b3 | ||
|
|
f92309101b | ||
|
|
4b7cc043ba | ||
|
|
104f976f1b | ||
|
|
7819b19d1c | ||
|
|
bb501c4b73 | ||
|
|
416392ad34 | ||
|
|
ad6023c3db | ||
|
|
7d3fbfb311 | ||
|
|
a90700fc6b | ||
|
|
401e0047ee | ||
|
|
1fd5ae2991 | ||
|
|
3abd177fc4 | ||
|
|
116e51b006 | ||
|
|
4620c59dde | ||
|
|
6384acd9bb | ||
|
|
310ecbb87c | ||
|
|
775721abdb | ||
|
|
9ebdbc58da | ||
|
|
9b0b6a44af | ||
|
|
7cb40f3c77 | ||
|
|
0d94e4c9ce | ||
|
|
a8e4d7a71a | ||
|
|
d66d2cb9d2 | ||
|
|
8b1860d5ec | ||
|
|
d96b077d22 | ||
|
|
5ae6305eb4 | ||
|
|
a9a00d76ae | ||
|
|
369c851532 | ||
|
|
902baa0bf4 | ||
|
|
6c7a6dcd8b | ||
|
|
086c9f431e | ||
|
|
d07e0eda42 | ||
|
|
666f1fbfb1 | ||
|
|
98e615fa5f | ||
|
|
3bb4aab296 | ||
|
|
b6b327ff22 | ||
|
|
170156cb41 | ||
|
|
515c8c2e32 | ||
|
|
16dd539401 | ||
|
|
41ae3a87db | ||
|
|
bee6a7a2b3 | ||
|
|
0c55a60897 | ||
|
|
ea6a29e8ab | ||
|
|
be53bae82b | ||
|
|
2e05afe35a | ||
|
|
c0cf93494c | ||
|
|
c368c5cae3 | ||
|
|
b5634d507b | ||
|
|
7fce57f35d | ||
|
|
bd15517d3e | ||
|
|
e444ba6886 | ||
|
|
70c98b0cfc | ||
|
|
dc25883685 | ||
|
|
7b323114e6 | ||
|
|
40016f48c1 | ||
|
|
f2c545db64 | ||
|
|
8ce669da6f | ||
|
|
ce6c2d2794 | ||
|
|
41ccb6a72b | ||
|
|
9e828ecbe7 | ||
|
|
dea03f5396 | ||
|
|
64ba33f3aa | ||
|
|
ce59bcce7a | ||
|
|
3cf26d19bc | ||
|
|
9ad7f324a2 | ||
|
|
fb8c5f6140 | ||
|
|
c309896620 | ||
|
|
71c7a48075 | ||
|
|
54f6556204 | ||
|
|
5b98547fe1 | ||
|
|
d2c4bb9872 | ||
|
|
7c578b38e6 | ||
|
|
3b71e34aa4 | ||
|
|
2c24fbb70b | ||
|
|
cdc44230cf | ||
|
|
0905ee37d7 | ||
|
|
5310e2cf7c | ||
|
|
a107f592cf | ||
|
|
8f831b2be3 | ||
|
|
742b49723f | ||
|
|
254a075d7d | ||
|
|
abdd612a9b | ||
|
|
e8a9f3f9c4 | ||
|
|
a456634324 | ||
|
|
6b228bf57b | ||
|
|
38041ce5c8 | ||
|
|
3ad0bd113f | ||
|
|
f1afc45ae2 | ||
|
|
9e2c672ac7 | ||
|
|
f055c23e9a | ||
|
|
d9f53d6e23 | ||
|
|
105ae1caec | ||
|
|
2bde8a8387 | ||
|
|
ee07af85fc | ||
|
|
b466ce029c | ||
|
|
94b2db96c2 | ||
|
|
372939c727 | ||
|
|
392a6c04dd | ||
|
|
616e813229 | ||
|
|
e1c6974b88 | ||
|
|
4830f89ed3 | ||
|
|
5a8bbf6457 | ||
|
|
da03611109 | ||
|
|
02838cc1b2 | ||
|
|
192c922e9f | ||
|
|
3b14ccb7d4 | ||
|
|
d41aff1861 | ||
|
|
8d0581e401 | ||
|
|
e95be7d922 | ||
|
|
5a5c11ac16 | ||
|
|
69607b4ade | ||
|
|
43a5d419e8 | ||
|
|
1c0b49d16d | ||
|
|
4327ff912f | ||
|
|
153ac5c60d | ||
|
|
8108bf0332 | ||
|
|
774a816055 | ||
|
|
b8058c51e5 | ||
|
|
2255c789df | ||
|
|
b55d2c1b6d | ||
|
|
57e0679524 | ||
|
|
24ed82c460 | ||
|
|
5670ba0578 | ||
|
|
94f63e67ef | ||
|
|
fb8db21593 | ||
|
|
55dbe18466 | ||
|
|
b0f2e2a1fb | ||
|
|
c451c9a268 | ||
|
|
426e7cb77b | ||
|
|
dd4dcebdeb | ||
|
|
e1d005baaa | ||
|
|
3d297e825f | ||
|
|
f1322175da | ||
|
|
2bad41082b | ||
|
|
d7d50699b6 | ||
|
|
ad986fe852 | ||
|
|
958c891039 | ||
|
|
d352c8a8d5 | ||
|
|
52b7bb50d0 | ||
|
|
cc57a9f347 | ||
|
|
363c073839 | ||
|
|
34a80d4b63 | ||
|
|
cd5e6f2542 | ||
|
|
2d7bc4fb16 | ||
|
|
0c435b9c3b | ||
|
|
e782e4744f | ||
|
|
0d24ebd6f5 | ||
|
|
c31055f144 | ||
|
|
1a28c3a909 | ||
|
|
d39e6a842c | ||
|
|
8013276515 | ||
|
|
4a87e73a45 | ||
|
|
ec1c4fd5c3 | ||
|
|
f645c2ce53 | ||
|
|
540738101f | ||
|
|
8bca5cd13e | ||
|
|
ff949392ed | ||
|
|
70286f57dc | ||
|
|
f58aa0c818 | ||
|
|
2d58f48b72 | ||
|
|
30ffcafc04 | ||
|
|
981e82eb45 | ||
|
|
02bc2f6390 | ||
|
|
e056dc15c9 | ||
|
|
15107f0f5c | ||
|
|
c61dd1e261 | ||
|
|
95615416a6 | ||
|
|
61c8cf5ccd | ||
|
|
fd58c57163 | ||
|
|
1be5593d9d | ||
|
|
957ba08817 | ||
|
|
f1938f5151 | ||
|
|
94e225411f | ||
|
|
93016d6174 | ||
|
|
301ff91f03 | ||
|
|
318df0d3f3 | ||
|
|
63092dd1fa | ||
|
|
f039fb36fc | ||
|
|
746dc72439 | ||
|
|
60ffee2c5e | ||
|
|
e80ab77cab | ||
|
|
f8fc5ca16c | ||
|
|
13f26a20d6 | ||
|
|
f839903497 | ||
|
|
103709eb8f | ||
|
|
26b975597b | ||
|
|
0792819fbd | ||
|
|
99e0fff296 | ||
|
|
cdbcdea2d2 | ||
|
|
a6c27841e1 | ||
|
|
ce84dd1377 | ||
|
|
0fdd785ba9 | ||
|
|
40724a5863 | ||
|
|
648644848a | ||
|
|
ff3292041f | ||
|
|
61cf313d67 | ||
|
|
5bcaa5540f | ||
|
|
0ca4bd9cca | ||
|
|
016b5f540b | ||
|
|
32928641a6 | ||
|
|
ca94367df6 | ||
|
|
da61d86e15 | ||
|
|
aee9130ce4 | ||
|
|
2349a1838c | ||
|
|
a64b3365a6 | ||
|
|
1fd7359fbb | ||
|
|
9ee624de43 | ||
|
|
a7b46eb004 | ||
|
|
3ae1f63679 | ||
|
|
1602c06285 | ||
|
|
6bc186512e | ||
|
|
72e3106e0d | ||
|
|
6bd7342d0e | ||
|
|
e6941e76a1 | ||
|
|
f9cfb5478e | ||
|
|
3c6bbb532f | ||
|
|
05c9c47ff9 | ||
|
|
820589f5a2 | ||
|
|
ed695cf26c | ||
|
|
d1e829f49a | ||
|
|
fe0168b504 | ||
|
|
e845b2f3a6 | ||
|
|
7f0ecf59d0 | ||
|
|
c8f35bd5b2 | ||
|
|
a2d243664b | ||
|
|
74c32915f1 | ||
|
|
5d3edc18d9 | ||
|
|
85ce13021e | ||
|
|
c64ece41e0 | ||
|
|
0512c7f22f | ||
|
|
92af7f37c5 | ||
|
|
f8d56bb111 | ||
|
|
11ce5aac4a | ||
|
|
59c7192452 | ||
|
|
6ceef6a573 | ||
|
|
425d81256b | ||
|
|
8f624e115b | ||
|
|
fc6c546c88 | ||
|
|
092d31e59c | ||
|
|
eb6b8d408a | ||
|
|
07bf57b161 | ||
|
|
e2bd17d253 | ||
|
|
35e762453b | ||
|
|
107513c4c8 | ||
|
|
5f4cf9abf9 | ||
|
|
60a73c74a5 | ||
|
|
7316e1c1ae | ||
|
|
19300ccdd7 | ||
|
|
902e74a943 | ||
|
|
a962df5f71 | ||
|
|
426449d1d8 | ||
|
|
287c32cbbe | ||
|
|
f9056d1dca | ||
|
|
977c829155 | ||
|
|
333804a636 | ||
|
|
c77285643b | ||
|
|
1ff930c846 | ||
|
|
343277f5b6 | ||
|
|
ba7f655de9 | ||
|
|
a00a35a02c | ||
|
|
3199baa14f | ||
|
|
33486ee2eb | ||
|
|
0a559a3562 | ||
|
|
706ecc6b40 | ||
|
|
3f3dfaf952 | ||
|
|
42e56632b5 | ||
|
|
90a4f4d756 | ||
|
|
eca1626bea | ||
|
|
8777d534eb | ||
|
|
b426ec00a0 | ||
|
|
5372c3e359 | ||
|
|
2fcbaee0b6 | ||
|
|
4cbf3b1e12 | ||
|
|
27be739d5e | ||
|
|
722822dc31 | ||
|
|
c3ab01afd4 | ||
|
|
2d6dcf976a | ||
|
|
c329b487a9 | ||
|
|
aa9fd06be6 | ||
|
|
36873be7ea | ||
|
|
50f06dbb7a | ||
|
|
e2d21d7d6e | ||
|
|
eeb7ba120f | ||
|
|
5c70875098 | ||
|
|
6d3e4b3757 | ||
|
|
1b9e5bc086 | ||
|
|
1f139d9337 | ||
|
|
3e41a3a5f9 | ||
|
|
f9da67ec3e | ||
|
|
a45fab4e43 | ||
|
|
7350d4e447 | ||
|
|
c3435c30ef | ||
|
|
8cbb28fb98 | ||
|
|
9d43ad32b4 | ||
|
|
5e14ee478e | ||
|
|
f70e247906 | ||
|
|
deb1c95c47 | ||
|
|
b0df349a23 | ||
|
|
ba620b28a3 | ||
|
|
4e26ec98b6 | ||
|
|
af599ceba8 | ||
|
|
55e73c7c67 | ||
|
|
521eb439b0 | ||
|
|
814bb5c6b6 | ||
|
|
7c45ca4d0f | ||
|
|
55c8caf0cc | ||
|
|
71155a0bf3 | ||
|
|
bc1b62f7da | ||
|
|
e9196bfce4 | ||
|
|
79c5683574 | ||
|
|
3f3858f0b3 | ||
|
|
5b680d28d0 | ||
|
|
6cdf77fec4 | ||
|
|
f8af73bce9 | ||
|
|
7eb59c5290 | ||
|
|
c407c83615 | ||
|
|
66d3f03f35 | ||
|
|
7d5683262e | ||
|
|
f03a68fbfe | ||
|
|
fc8397de79 | ||
|
|
51ea3b2cbc | ||
|
|
46c3da70a9 | ||
|
|
7d66e995c3 | ||
|
|
5d5a7b88e0 | ||
|
|
886b0a6373 | ||
|
|
d3e8e5ff21 | ||
|
|
7a128fb7cc | ||
|
|
4ff32612c8 | ||
|
|
a32f341f31 | ||
|
|
2add8f3601 | ||
|
|
fdd3d2d54b | ||
|
|
b4ff3b1c35 | ||
|
|
859444703f | ||
|
|
c1ef83eec7 | ||
|
|
1a81f10d89 | ||
|
|
96818cd5ad | ||
|
|
1e5e84b10a | ||
|
|
8b170cbbd2 | ||
|
|
53982458cd | ||
|
|
9df694a253 | ||
|
|
76695a032a |
@@ -1,9 +1,11 @@
|
||||
_APP_ENV=development
|
||||
_APP_LOCALE=en
|
||||
_APP_WORKER_PER_CORE=6
|
||||
_APP_WORKER_PER_CORE=2
|
||||
_APP_CONSOLE_WHITELIST_ROOT=disabled
|
||||
_APP_CONSOLE_WHITELIST_EMAILS=
|
||||
_APP_CONSOLE_WHITELIST_CODES=code-zero,code-one
|
||||
_APP_CONSOLE_WHITELIST_IPS=
|
||||
_APP_CONSOLE_INVITES=enabled
|
||||
_APP_SYSTEM_EMAIL_NAME=Appwrite
|
||||
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
|
||||
@@ -23,15 +25,15 @@ _APP_DB_SCHEMA=appwrite
|
||||
_APP_DB_USER=user
|
||||
_APP_DB_PASS=password
|
||||
_APP_DB_ROOT_PASS=rootsecretpassword
|
||||
_APP_STORAGE_DEVICE=Local
|
||||
_APP_STORAGE_S3_ACCESS_KEY=
|
||||
_APP_STORAGE_S3_SECRET=
|
||||
_APP_STORAGE_S3_REGION=us-east-1
|
||||
_APP_STORAGE_S3_BUCKET=
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY=
|
||||
_APP_STORAGE_DO_SPACES_SECRET=
|
||||
_APP_STORAGE_DO_SPACES_REGION=us-east-1
|
||||
_APP_STORAGE_DO_SPACES_BUCKET=
|
||||
_APP_CONNECTIONS_MAX=3100
|
||||
_APP_POOL_CLIENTS=14
|
||||
_APP_CONNECTIONS_DB_REPLICAS=db_fra1_02=mariadb://root:rootsecretpassword@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_DB_PROJECT=db_fra1_02=mariadb://user:password@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_DB_CONSOLE=db_fra1_01=mariadb://user:password@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_CACHE=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_QUEUE=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_PUBSUB=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_STORAGE=local://localhost
|
||||
_APP_STORAGE_ANTIVIRUS=disabled
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
@@ -44,27 +46,42 @@ _APP_SMTP_PORT=1025
|
||||
_APP_SMTP_SECURE=
|
||||
_APP_SMTP_USERNAME=
|
||||
_APP_SMTP_PASSWORD=
|
||||
_APP_USERS_STATS_RECIPIENTS=
|
||||
_APP_HAMSTER_INTERVAL=86400
|
||||
_APP_HAMSTER_TIME=21:00
|
||||
_APP_MIXPANEL_TOKEN=
|
||||
_APP_SMS_PROVIDER=sms://username:password@mock
|
||||
_APP_SMS_FROM=+123456789
|
||||
_APP_STORAGE_LIMIT=30000000
|
||||
_APP_STORAGE_PREVIEW_LIMIT=20000000
|
||||
_APP_FUNCTIONS_SIZE_LIMIT=30000000
|
||||
_APP_FUNCTIONS_TIMEOUT=900
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT=900
|
||||
_APP_FUNCTIONS_CONTAINERS=10
|
||||
_APP_FUNCTIONS_CPUS=0
|
||||
_APP_FUNCTIONS_MEMORY=0
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=0
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
||||
OPEN_RUNTIMES_NETWORK=appwrite_runtimes
|
||||
_APP_FUNCTIONS_CPUS=1
|
||||
_APP_FUNCTIONS_MEMORY=512
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=600
|
||||
_APP_FUNCTIONS_MAINTENANCE_INTERVAL=600
|
||||
_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes
|
||||
_APP_EXECUTOR_SECRET=your-secret-key
|
||||
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
||||
_APP_EXECUTOR_HOST=http://exc1/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
|
||||
_APP_USAGE_AGGREGATION_INTERVAL=30
|
||||
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
|
||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||
_APP_USAGE_STATS=enabled
|
||||
_APP_USAGE_AGGREGATION_INTERVAL=30
|
||||
_APP_LOGGING_PROVIDER=
|
||||
_APP_LOGGING_CONFIG=
|
||||
DOCKERHUB_PULL_USERNAME=
|
||||
DOCKERHUB_PULL_PASSWORD=
|
||||
DOCKERHUB_PULL_EMAIL=
|
||||
_APP_REGION=default
|
||||
_APP_DOCKER_HUB_USERNAME=
|
||||
_APP_DOCKER_HUB_PASSWORD=
|
||||
_APP_CONSOLE_GITHUB_SECRET=
|
||||
_APP_CONSOLE_GITHUB_APP_ID=
|
||||
_DO_SPACES_BUCKET_NAME=backups-v1
|
||||
_DO_SPACES_REGION=fra1
|
||||
_DO_SPACES_ACCESS_KEY=
|
||||
_DO_SPACES_SECRET_KEY=
|
||||
@@ -22,3 +22,7 @@ tests/*/*/* linguist-detectable=false
|
||||
tests/*/*/*/* linguist-detectable=false
|
||||
tests/*/*/*/*/* linguist-detectable=false
|
||||
tests/*/*/*/*/*/* linguist-detectable=false
|
||||
public/scripts/* linguist-detectable=false
|
||||
public/scripts/*/*/* linguist-detectable=false
|
||||
public/scripts/*/*/*/* linguist-detectable=false
|
||||
public/dist/scripts/* linguist-detectable=false
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
name: "🐛 Bug Report"
|
||||
description: "Submit a bug report to help us improve"
|
||||
title: "🐛 Bug Report: "
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out our bug report form 🙏
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "👟 Reproduction steps"
|
||||
description: "How do you trigger this bug? Please walk us through it step by step."
|
||||
placeholder: "When I ..."
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "👍 Expected behavior"
|
||||
description: "What did you think would happen?"
|
||||
placeholder: "It should ..."
|
||||
- type: textarea
|
||||
id: actual-behavior
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "👎 Actual Behavior"
|
||||
description: "What did actually happen? Add screenshots, if applicable."
|
||||
placeholder: "It actually ..."
|
||||
- type: dropdown
|
||||
id: appwrite-version
|
||||
attributes:
|
||||
label: "🎲 Appwrite version"
|
||||
description: "What version of Appwrite are you running?"
|
||||
options:
|
||||
- Version 0.14.x
|
||||
- Version 0.13.x
|
||||
- Version 0.12.x
|
||||
- Version 0.11.x
|
||||
- Version 0.10.x
|
||||
- Version 0.9.x
|
||||
- Version 0.8.x
|
||||
- Version 0.7.x
|
||||
- Version 0.6.x
|
||||
- Different version (specify in environment)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: operating-system
|
||||
attributes:
|
||||
label: "💻 Operating system"
|
||||
description: "What OS is your server / device running on?"
|
||||
options:
|
||||
- Linux
|
||||
- MacOS
|
||||
- Windows
|
||||
- Something else
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: environment
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "🧱 Your Environment"
|
||||
description: "Is your environment customized in any way?"
|
||||
placeholder: "I use Cloudflare for ..."
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "👀 Have you spent some time to check if this issue has been raised before?"
|
||||
description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
|
||||
options:
|
||||
- label: "I checked and didn't find similar issue"
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: read-code-of-conduct
|
||||
attributes:
|
||||
label: "🏢 Have you read the Code of Conduct?"
|
||||
options:
|
||||
- label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
|
||||
required: true
|
||||
@@ -1,32 +0,0 @@
|
||||
name: "📚 Documentation"
|
||||
description: "Report an issue related to documentation"
|
||||
title: "📚 Documentation: "
|
||||
labels: [documentation]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to make our documentation better 🙏
|
||||
- type: textarea
|
||||
id: issue-description
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "💭 Description"
|
||||
description: "A clear and concise description of what the issue is."
|
||||
placeholder: "Documentation should not ..."
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "👀 Have you spent some time to check if this issue has been raised before?"
|
||||
description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
|
||||
options:
|
||||
- label: "I checked and didn't find similar issue"
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: read-code-of-conduct
|
||||
attributes:
|
||||
label: "🏢 Have you read the Code of Conduct?"
|
||||
options:
|
||||
- label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
|
||||
required: true
|
||||
@@ -1,40 +0,0 @@
|
||||
name: 🚀 Feature
|
||||
description: "Submit a proposal for a new feature"
|
||||
title: "🚀 Feature: "
|
||||
labels: [feature]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out our feature request form 🙏
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "🔖 Feature description"
|
||||
description: "A clear and concise description of what the feature is."
|
||||
placeholder: "You should add ..."
|
||||
- type: textarea
|
||||
id: pitch
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "🎤 Pitch"
|
||||
description: "Please explain why this feature should be implemented and how it would be used. Add examples, if applicable."
|
||||
placeholder: "In my use-case, ..."
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "👀 Have you spent some time to check if this issue has been raised before?"
|
||||
description: "Have you Googled for a similar issue or checked our older issues for a similar bug?"
|
||||
options:
|
||||
- label: "I checked and didn't find similar issue"
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: read-code-of-conduct
|
||||
attributes:
|
||||
label: "🏢 Have you read the Code of Conduct?"
|
||||
options:
|
||||
- label: "I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)"
|
||||
required: true
|
||||
@@ -21,6 +21,11 @@ Happy contributing!
|
||||
|
||||
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
|
||||
|
||||
### Have you added your change to the [Changelog](https://github.com/appwrite/appwrite/blob/master/CHANGES.md)?
|
||||
|
||||
(The CHANGES.md file tracks all the changes that make it to the `main` branch. Add your change to this file in the following format)
|
||||
- One line description of your PR [#pr_number](Link to your PR)
|
||||
|
||||
### Have you read the [Contributing Guidelines on issues](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md)?
|
||||
|
||||
(Write your answer here.)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
name: "Linter"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
name: Linter
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- run: git checkout HEAD^2
|
||||
|
||||
- name: Run Linter
|
||||
run: |
|
||||
docker run --rm -v $PWD:/app composer sh -c \
|
||||
"composer install --profile --ignore-platform-reqs && composer lint"
|
||||
@@ -0,0 +1,46 @@
|
||||
name: "Build and Publish"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- appwrite-*
|
||||
|
||||
jobs:
|
||||
build-publish:
|
||||
name: Build and Publish
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
submodules: recursive
|
||||
ref: db-pools-backups
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: appwrite/cloud
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
|
||||
- name: Build & Publish to DockerHub
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
|
||||
VITE_GA_PROJECT=G-L7G2B6PLDS
|
||||
VITE_CONSOLE_MODE=cloud
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
+10
-12
@@ -4,7 +4,7 @@ on: [pull_request]
|
||||
jobs:
|
||||
tests:
|
||||
name: Unit & E2E
|
||||
runs-on: self-hosted
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -13,6 +13,8 @@ jobs:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
# Fetch submodules
|
||||
submodules: recursive
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
@@ -23,12 +25,15 @@ jobs:
|
||||
# Upstream bug causes buildkit pulls to fail so prefetch base images
|
||||
# https://github.com/moby/moby/issues/41864
|
||||
run: |
|
||||
echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env
|
||||
export COMPOSE_INTERACTIVE_NO_CLI
|
||||
export DOCKER_BUILDKIT=1
|
||||
export COMPOSE_DOCKER_CLI_BUILD=1
|
||||
export BUILDKIT_PROGRESS=plain
|
||||
docker pull composer:2.0
|
||||
docker pull php:8.0-cli-alpine
|
||||
docker compose build --progress=plain
|
||||
docker compose build appwrite
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
sleep 30
|
||||
|
||||
- name: Doctor
|
||||
run: docker compose exec -T appwrite doctor
|
||||
|
||||
@@ -37,10 +42,3 @@ jobs:
|
||||
|
||||
- name: Run Tests
|
||||
run: docker compose exec -T appwrite test --debug
|
||||
|
||||
- name: Teardown
|
||||
if: always()
|
||||
run: |
|
||||
docker ps -aq | xargs docker rm --force
|
||||
docker volume prune --force
|
||||
docker network prune --force
|
||||
@@ -10,3 +10,5 @@
|
||||
debug/
|
||||
app/sdks
|
||||
dev/yasd_init.php
|
||||
.phpunit.result.cache
|
||||
/backup-data/
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = cloud-cards
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
# Disable current PHP installation
|
||||
RUN sudo a2dismod php7.4
|
||||
RUN sudo a2dismod mpm_prefork
|
||||
|
||||
# Install apache2 (PHP install requires to do this first)
|
||||
RUN sudo apt --yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install apache2
|
||||
|
||||
# Update to PHP 8.0 with unattended installation
|
||||
RUN sudo apt --yes install software-properties-common && sudo add-apt-repository ppa:ondrej/php -y
|
||||
RUN sudo apt update
|
||||
RUN sudo apt --yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install php8.0
|
||||
+16
-6
@@ -1,10 +1,19 @@
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
tasks:
|
||||
- init: docker-compose pull &&
|
||||
docker-compose build &&
|
||||
docker run --rm --interactive --tty --volume $PWD:/app composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist
|
||||
- name: Run Appwrite Docker Stack
|
||||
init: |
|
||||
git submodule update --init
|
||||
docker compose build
|
||||
docker compose pull
|
||||
docker pull composer
|
||||
command: |
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
composer update \
|
||||
--ignore-platform-reqs \
|
||||
--optimize-autoloader \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
|
||||
ports:
|
||||
- port: 8080
|
||||
@@ -14,6 +23,7 @@ ports:
|
||||
vscode:
|
||||
extensions:
|
||||
- ms-azuretools.vscode-docker
|
||||
- zobo.php-intellisense
|
||||
|
||||
github:
|
||||
# https://www.gitpod.io/docs/prebuilds#github-specific-configuration
|
||||
|
||||
+234
@@ -1,3 +1,237 @@
|
||||
# TBD
|
||||
- Replace Appwrite executor with OpenRuntimes Executor [#4650](https://github.com/appwrite/appwrite/pull/4650)
|
||||
- Add `_APP_CONNECTIONS_MAX` env var [#4673](https://github.com/appwrite/appwrite/pull/4673)
|
||||
- Increase Traefik TCP + file limits [#4673](https://github.com/appwrite/appwrite/pull/4673)
|
||||
- Fix invited account verified status [#4776](https://github.com/appwrite/appwrite/pull/4776)
|
||||
- Get default region from environment on project create [#4780](https://github.com/appwrite/appwrite/pull/4780)
|
||||
- Fix max mimetype size [#4814](https://github.com/appwrite/appwrite/pull/4814)
|
||||
|
||||
# Version 1.1.2
|
||||
## Changes
|
||||
- Released `appwrite/console` [2.0.2](https://github.com/appwrite/console/releases/tag/2.0.2)
|
||||
- Make `region` parameter optional with default for project create [#4763](https://github.com/appwrite/appwrite/pull/4763)
|
||||
|
||||
## Bugs
|
||||
- Fix default oauth paths [#4725](https://github.com/appwrite/appwrite/pull/4725)
|
||||
- Fix session expiration, and expired session deletion [#4739](https://github.com/appwrite/appwrite/pull/4739)
|
||||
- Fix processing status on sync executions [#4737](https://github.com/appwrite/appwrite/pull/4737)
|
||||
- Fix Locale API returning Unknown continent [#4761](https://github.com/appwrite/appwrite/pull/4761)
|
||||
|
||||
# Version 1.1.1
|
||||
## Bugs
|
||||
- Fix Deletes worker using incorrect device for file deletion [#4662](https://github.com/appwrite/appwrite/pull/4662)
|
||||
- Fix Migration for Stats adding the region attribute [#4704](https://github.com/appwrite/appwrite/pull/4704)
|
||||
- Fix Migration stopping scheduled functions [#4704](https://github.com/appwrite/appwrite/pull/4704)
|
||||
- Fix Migration enabling OAuth providers with data by default [#4704](https://github.com/appwrite/appwrite/pull/4704)
|
||||
- Fix Error pages when OAuth providers are disabled [#4704](https://github.com/appwrite/appwrite/pull/4704)
|
||||
|
||||
# Version 1.1.0
|
||||
## Features
|
||||
- Added support for the new Appwrite Console [#4655](https://github.com/appwrite/appwrite/pull/4655)
|
||||
- Added new property to projects configuration: `authDuration` which allows you to alter the duration of signed in sessions for your project. [#4618](https://github.com/appwrite/appwrite/pull/4618)
|
||||
|
||||
## Bugs
|
||||
- Fix license detection for Flutter and Dart SDKs [#4435](https://github.com/appwrite/appwrite/pull/4435)
|
||||
- Fix missing `status`, `buildStderr` and `buildStderr` from get deployment response [#4611](https://github.com/appwrite/appwrite/pull/4611)
|
||||
- Fix project pagination in DB usage aggregation [#4517](https://github.com/appwrite/appwrite/pull/4517)
|
||||
- Fix missing file permissions due to cache [#4661](https://github.com/appwrite/appwrite/pull/4661)
|
||||
- Fix usage stats for async function executions [#4674](https://github.com/appwrite/appwrite/pull/4674)
|
||||
|
||||
# Features
|
||||
- Added Auth Duration API to allow users to set the duration of their sessions. [#4618](https://github.com/appwrite/appwrite/pull/4618)
|
||||
|
||||
# Version 1.0.3
|
||||
## Bugs
|
||||
- Fix document audit deletion [#4429](https://github.com/appwrite/appwrite/pull/4429)
|
||||
- Fix attribute and index deletion when deleting a collection [#4429](https://github.com/appwrite/appwrite/pull/4429)
|
||||
|
||||
# Version 1.0.2
|
||||
## Bugs
|
||||
- Fixed nullable values in functions variables [#3885](https://github.com/appwrite/appwrite/pull/3885)
|
||||
- Fixed migration for audit by migrating the `time` attribute [#4038](https://github.com/appwrite/appwrite/pull/4038)
|
||||
- Fixed default value for creating Boolean Attribute [#4040](https://github.com/appwrite/appwrite/pull/4040)
|
||||
- Fixed phone authentication code to be hashed in the internal database [#3906](https://github.com/appwrite/appwrite/pull/3906)
|
||||
- Fixed `/v1/teams/:teamId/memberships/:membershipId` response [#3883](https://github.com/appwrite/appwrite/pull/3883)
|
||||
- Fixed removing variables when function is deleted [#3884](https://github.com/appwrite/appwrite/pull/3884)
|
||||
- Fixed scheduled function not being triggered [#3908](https://github.com/appwrite/appwrite/pull/3908)
|
||||
- Fixed Phone Provider configuration [#3862](https://github.com/appwrite/appwrite/pull/3883)
|
||||
- Fixed Queries with `0` values [utopia-php/database#194](https://github.com/utopia-php/database/pull/194)
|
||||
|
||||
# Version 1.0.1
|
||||
## Bugs
|
||||
- Fixed migration for abuse by migrating the `time` attribute [#3839](https://github.com/appwrite/appwrite/pull/3839)
|
||||
|
||||
# Version 1.0.0
|
||||
## BREAKING CHANGES
|
||||
- All Date values are now stored as ISO-8601 instead of UNIX timestamps [#3516](https://github.com/appwrite/appwrite/pull/3516)
|
||||
- Permission levels and syntax have been reworked. See the Permissions V2 section in the document for more information [#3700](https://github.com/appwrite/appwrite/pull/3700)
|
||||
- Function Variables are now stored in a separate collection with their own API endpoints [#3634](https://github.com/appwrite/appwrite/pull/3634)
|
||||
- Resources that are computed asynchronously, such as function deployments, will now return a `202 Accepted` status code instead of `200 OK` [#3547](https://github.com/appwrite/appwrite/pull/3547)
|
||||
- Queries have been improved to allow even more flexibility, and introduced to new endpoints. See the Queries V2 section in the document for more information [#3702](https://github.com/appwrite/appwrite/pull/3702)
|
||||
- Compound indexes are now more flexible [#151](https://github.com/utopia-php/database/pull/151)
|
||||
- `createExecution` parameter `async` default value was changed from `true` to `false` [#3781](https://github.com/appwrite/appwrite/pull/3781)
|
||||
- String attribute `status` has been refactored to a Boolean attribute `enabled` in the functions collection [#3798](https://github.com/appwrite/appwrite/pull/3798)
|
||||
- `time` attribute in Execution response model has been renamed to `duration` to be more consistent with other response models. [#3801](https://github.com/appwrite/appwrite/pull/3801)
|
||||
- Renamed the following list endpoints to stay consistent with other endpoints [#3825](https://github.com/appwrite/appwrite/pull/3825)
|
||||
- `getMemberships` to `listMemberships` in Teams API
|
||||
- `getMemberships` to `listMemberships` in Users API
|
||||
- `getLogs` to `listLogs` in Users API
|
||||
- `getLogs` to `listLogs` in Accounts API
|
||||
- `getSessions` to `listSessions` in Accounts API
|
||||
- `getSessions` to `listSessions` in Users API
|
||||
- `getCountries` to `listCountries` in Locale API
|
||||
- `getCountriesEU` to `listCountriesEU` in Locale API
|
||||
- `getCountriesPhones` to `listCountriesPhones` in Locale API
|
||||
- `getContinents` to `listContinents` in Locale API
|
||||
- `getCurrencies` to `listCurrencies` in Locale API
|
||||
- `getLanguages` to `listLanguages` in Locale API
|
||||
|
||||
## Features
|
||||
- Added the UI to see the Parent ID of all resources within the UI. [#3653](https://github.com/appwrite/appwrite/pull/3653)
|
||||
- Added automatic cache cleaning for internal Appwrite services [#3491](https://github.com/appwrite/appwrite/pull/3491)
|
||||
- Added the ability for Appwrite to handle importing hashed passwords, this can be leveraged to import existing user data from other systems. More information can be found in the document linked above. [#2747](https://github.com/appwrite/appwrite/pull/2747)
|
||||
- `Users` has now been renamed to `Authentication` within the Appwrite console [#3664](https://github.com/appwrite/appwrite/pull/3664)
|
||||
- More endpoints were made public (for guests) with proper rate limits [#3741](https://github.com/appwrite/appwrite/pull/3741)
|
||||
- Added Disqus, Podio, and Etsy OAuth providers [#3526](https://github.com/appwrite/appwrite/pull/3526), [#3488](https://github.com/appwrite/appwrite/pull/3488), [#3522](https://github.com/appwrite/appwrite/pull/3522)
|
||||
- Function logs now capture stdout [#3656](https://github.com/appwrite/appwrite/pull/3656)
|
||||
- Added the ability to grant guests write permissions for documents, files and executions [#3727](https://github.com/appwrite/appwrite/pull/3727)
|
||||
|
||||
## Bugs
|
||||
- Fixed an issue where after resetting your password in the Appwrite console, you would not be redirected to the login page. [#3654](https://github.com/appwrite/appwrite/pull/3654)
|
||||
- Fixed an issue where invalid data could be loaded into the Appwrite console. [#3660](https://github.com/appwrite/appwrite/pull/3660)
|
||||
- Fixed an issue where users using the MySQL adapter for Appwrite would run into an issue with full text indexes [#154](https://github.com/utopia-php/database/pull/154)
|
||||
- Fix teams being created with no owners [#3558](https://github.com/appwrite/appwrite/pull/3558)
|
||||
- Fixed a bug where you could not search users by phone [#3619](https://github.com/appwrite/appwrite/pull/3619)
|
||||
- Fixed a bug where unaccepted invitations would grant access to projects [#3738](https://github.com/appwrite/appwrite/pull/3738)
|
||||
|
||||
# Version 0.15.3
|
||||
## Features
|
||||
- Added hint during Installation for DNS Configuration by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/2450
|
||||
## Bugs
|
||||
- Fixed Migration for Attributes and Indexes by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3568
|
||||
- Fixed Closed Icon in the alerts to be centered by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3594
|
||||
- Fixed Response Model for Get and Update Database Endpoint by @ishanvyas22 in https://github.com/appwrite/appwrite/pull/3553
|
||||
- Fixed Missing Usage on Functions exection by @Meldiron in https://github.com/appwrite/appwrite/pull/3543
|
||||
- Fixed Validation for Permissions to only accept a maximum of 100 Permissions for all endpoints by @Meldiron in https://github.com/appwrite/appwrite/pull/3532
|
||||
- Fixed backwards compatibility for Create Email Session Endpoint by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3517
|
||||
|
||||
# Version 0.15.2
|
||||
## Bugs
|
||||
- Fixed Realtime Authentication for the Console by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3506
|
||||
- Fixed Collection Usage by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3505
|
||||
- Fixed `$createdAt` after updating document by @Meldiron in https://github.com/appwrite/appwrite/pull/3498
|
||||
- Fixed Redirect after deleting Collection in Console @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3476
|
||||
- Fixed broken Link for Documents under Collections by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3469
|
||||
|
||||
# Version 0.15.1
|
||||
## Bugs
|
||||
- Fixed SMS for `createVerification` by @christyjacob4 in https://github.com/appwrite/appwrite/pull/3454
|
||||
- Fixed missing Attributes when creating an Index by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3461
|
||||
- Fixed broken Link for Documents under Collections by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3461
|
||||
- Fixed all `$createdAt` and `$updatedAt` occurences in the UI by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3461
|
||||
- Fixed Delete Document from the UI by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3463
|
||||
- Fixed internal Attribute and Index key on Migration by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3455
|
||||
|
||||
## Docs
|
||||
- Updated Phone Authentication by @christyjacob4 in https://github.com/appwrite/appwrite/pull/3456
|
||||
|
||||
# Version 0.15.0
|
||||
|
||||
## BREAKING CHANGES
|
||||
- Docker Compose V2 is required now
|
||||
- The `POST:/v1/account/sessions` endpoint is now `POST:/v1/account/sessions/email`
|
||||
- All `/v1/database/...` endpoints are now `/v1/databases/...`
|
||||
- `dateCreated` attribute is removed from Teams
|
||||
- `dateCreated` attribute is removed from Executions
|
||||
- `dateCreated` attribute is removed from Files
|
||||
- `dateCreated` and `dateUpdated` attributes are removed from Functions
|
||||
- `dateCreated` and `dateUpdated` attributes are removed from Deployments
|
||||
- `dateCreated` and `dateUpdated` attributes are removed from Buckets
|
||||
- Following Events for Webhooks and Functions are changed:
|
||||
- `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID]`
|
||||
- `collections.[COLLECTION_ID].documents.[DOCUMENT_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID].documents.[DOCUMENT_ID]`
|
||||
- Following Realtime Channels are changed:
|
||||
- `collections.[COLLECTION_ID]` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID]`
|
||||
- `collections.[COLLECTION_ID].documents` is now `databases.[DATABASE_ID].collections.[COLLECTION_ID].documents`
|
||||
- After Migration a Database called `default` is created for all your existing Database Collections
|
||||
|
||||
## Features
|
||||
- Added Phone Authentication by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3357
|
||||
- Added Twilio Support
|
||||
- Added TextMagic Support
|
||||
- Added Telesign Support
|
||||
- Added Endpoint to create Phone Session (`POST:/v1/account/sessions/phone`)
|
||||
- Added Endpoint to confirm Phone Session (`PUT:/v1/account/sessions/phone`)
|
||||
- Added Endpoint to update Account Phone Number (`PATCH:/v1/account/phone`)
|
||||
- Added Endpoint to create Account Phone Verification (`POST:/v1/account/verification/phone`)
|
||||
- Added Endpoint to confirm Account Phone Verification (`PUT:/v1/account/verification/phone`)
|
||||
- Added `_APP_PHONE_PROVIDER` and `_APP_PHONE_FROM` Environment Variable
|
||||
- Added `phone` and `phoneVerification` Attribute to User
|
||||
- Added `$createdAt` and `$updatedAt` Attributes by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3382
|
||||
- Bucket
|
||||
- Collection
|
||||
- Deployment
|
||||
- Document
|
||||
- Domain
|
||||
- Execution
|
||||
- File
|
||||
- Func
|
||||
- Key
|
||||
- Membership
|
||||
- Platform
|
||||
- Project
|
||||
- Team
|
||||
- User
|
||||
- Webhook
|
||||
- Session (only `$createdAt`)
|
||||
- Token (only `$createdAt`)
|
||||
- Added Databases Resource to the Database Service by @lohanidamodar in https://github.com/appwrite/appwrite/pull/3338
|
||||
- Added `databases.read` and `databases.write` Scopes for API Keys
|
||||
- Added New Runtimes
|
||||
- Dart 2.17
|
||||
- Deno 1.21
|
||||
- Java 18
|
||||
- Node 18
|
||||
- Webhooks now have a Signature Key for proof of Origin by @shimonewman in https://github.com/appwrite/appwrite/pull/3351
|
||||
- Start using Docker Compose V2 (from `docker-compose` to `docker compose`) by @Meldiron in https://github.com/appwrite/appwrite/pull/3362
|
||||
- Added support for selfhosted Gitlab (OAuth) by @Meldiron in https://github.com/appwrite/appwrite/pull/3366
|
||||
- Added Dailymotion OAuth Provider by @2002Bishwajeet in https://github.com/appwrite/appwrite/pull/3371
|
||||
- Added Autodesk OAuth Provider by @Haimantika in https://github.com/appwrite/appwrite/pull/3420
|
||||
- Ignore Service Checks when using API Key by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3270
|
||||
- Added WebM as MIME- and Preview Type by @chuongtang in https://github.com/appwrite/appwrite/pull/3327
|
||||
- Expired User Sessions are now deleted by the Maintenance Worker by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3324
|
||||
- Increased JWT rate-limit to 100 per hour by @abnegate in https://github.com/appwrite/appwrite/pull/3345
|
||||
- Internal Database Relations are now resolved using the Internal ID by @fogelito in https://github.com/appwrite/appwrite/pull/3383
|
||||
- Permissions for Documents can be updated without payload now by @gepd in https://github.com/appwrite/appwrite/pull/3346
|
||||
|
||||
## Bugs
|
||||
- Fixed Zoom OAuth scopes
|
||||
- Fixed empty build logs for Functions
|
||||
- Fixed unnecessary SMTP check on Team Invite using an API Key by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3270
|
||||
- Fixed Error Message when adding Team Member to project by @stnguyen90 in https://github.com/appwrite/appwrite/pull/3296
|
||||
- Fixed .NET Runtime Logo by @adityaoberai in https://github.com/appwrite/appwrite/pull/3315
|
||||
- Fixed unnecessary Function execution delays by @Meldiron in https://github.com/appwrite/appwrite/pull/3348
|
||||
- Fixed Runtime race conditions on cold start by @PineappleIOnic in https://github.com/appwrite/appwrite/pull/3361
|
||||
- Fixed Malayalam translation by @varghesejose2020 in https://github.com/appwrite/appwrite/pull/2561
|
||||
- Fixed English translation by @MATsxm in https://github.com/appwrite/appwrite/pull/3337
|
||||
- Fixed spelling in Realtime Worker logs by @gireeshp in https://github.com/appwrite/appwrite/pull/1663
|
||||
- Fixed Docs URL for Yammer OAuth by @everly-gif in https://github.com/appwrite/appwrite/pull/3402
|
||||
|
||||
# Version 0.14.2
|
||||
|
||||
## Features
|
||||
|
||||
- Support for Backblaze adapter in Storage
|
||||
- Support for Linode adapter in Storage
|
||||
- Support for Wasabi adapter in Storage
|
||||
- New Cloud Function Runtimes:
|
||||
- Dart 2.17
|
||||
- Deno 1.21
|
||||
- Java 18
|
||||
- Node 18
|
||||
- Improved overall Migration speed
|
||||
|
||||
|
||||
# Version 0.14.1
|
||||
|
||||
## Bugs
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity, expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@appwrite.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
+115
-89
@@ -8,15 +8,16 @@ If you are worried or don’t know where to start, check out our next section ex
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Help us keep Appwrite open and inclusive. Please read and follow our [Code of Conduct](/CODE_OF_CONDUCT.md).
|
||||
Help us keep Appwrite open and inclusive. Please read and follow our [Code of Conduct](/https://github.com/appwrite/.github/blob/main/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Submit a Pull Request 🚀
|
||||
|
||||
Branch naming convention is as following
|
||||
Branch naming convention is as follows
|
||||
|
||||
`TYPE-ISSUE_ID-DESCRIPTION`
|
||||
|
||||
example:
|
||||
|
||||
```
|
||||
doc-548-submit-a-pull-request-section-to-contribution-guide
|
||||
```
|
||||
@@ -29,32 +30,55 @@ When `TYPE` can be:
|
||||
- **fix** - a bug fix
|
||||
- **refactor** - code change that neither fixes a bug nor adds a feature
|
||||
|
||||
**All PRs must include a commit message with the changes description!**
|
||||
**All PRs must include a commit message with the description of the changes made!**
|
||||
|
||||
For the initial start, fork the project and use git clone command to download the repository to your computer. A standard procedure for working on an issue would be to:
|
||||
|
||||
1. `git pull`, before creating a new branch, pull the changes from upstream. Your master needs to be up to date.
|
||||
|
||||
```
|
||||
$ git pull
|
||||
```
|
||||
|
||||
2. Create new branch from `master` like: `doc-548-submit-a-pull-request-section-to-contribution-guide`<br/>
|
||||
|
||||
```
|
||||
$ git checkout -b [name_of_your_new_branch]
|
||||
```
|
||||
|
||||
3. Work - commit - repeat ( be sure to be in your branch )
|
||||
|
||||
4. Push changes to GitHub
|
||||
4. Before you push your changes, make sure your code follows the `PSR12` coding standards, which is the standard Appwrite follows currently. You can easily do this by running the formatter.
|
||||
|
||||
```bash
|
||||
composer format <your file path>
|
||||
```
|
||||
|
||||
Now, go a step further by running the linter by the following command to manually fix the issues the formatter wasn't able to fix.
|
||||
|
||||
```bash
|
||||
composer lint <your file path>
|
||||
```
|
||||
|
||||
This will give you a list of errors for you to rectify, if there is an instance you need more information on the errors being displayed you can pass in additional command line arguments. More list of available arguments can be found [here](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues.
|
||||
|
||||
```bash
|
||||
composer lint --report=diff <your file path>
|
||||
```
|
||||
|
||||
5. Push changes to GitHub
|
||||
|
||||
```
|
||||
$ git push origin [name_of_your_new_branch]
|
||||
```
|
||||
|
||||
5. Submit your changes for review
|
||||
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
|
||||
6. Start a Pull Request
|
||||
Now submit the pull request and click on `Create pull request`.
|
||||
7. Get a code review approval/reject
|
||||
8. After approval, merge your PR
|
||||
9. GitHub will automatically delete the branch after the merge is done. (they can still be restored).
|
||||
6. Submit your changes for review
|
||||
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
|
||||
7. Start a Pull Request
|
||||
Now submit the pull request and click on `Create pull request`.
|
||||
8. Get a code review approval/reject
|
||||
9. After approval, merge your PR
|
||||
10. GitHub will automatically delete the branch after the merge is done. (they can still be restored).
|
||||
|
||||
## Setup From Source
|
||||
|
||||
@@ -69,7 +93,8 @@ git clone git@github.com:[YOUR_FORK_HERE]/appwrite.git
|
||||
|
||||
cd appwrite
|
||||
|
||||
docker-compose up -d
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Code Autocompletion
|
||||
@@ -84,24 +109,26 @@ docker run --rm --interactive --tty \
|
||||
|
||||
### User Interface
|
||||
|
||||
Appwrite uses an internal micro-framework called Litespeed.js to build simple UI components in vanilla JS and [less](http://lesscss.org/) for compiling CSS code. To apply any of your changes to the UI, use the `gulp build` or `gulp less` commands, and restart the Appwrite main container to load the new static files to memory using `docker-compose restart appwrite`.
|
||||
Appwrite uses an internal micro-framework called Litespeed.js to build simple UI components in vanilla JS and [less](http://lesscss.org/) for compiling CSS code. To apply any of your changes to the UI, use the `gulp build` or `gulp less` commands, and restart the Appwrite main container to load the new static files to memory using `docker compose restart appwrite`.
|
||||
|
||||
### Get Started
|
||||
|
||||
After finishing the installation process, you can start writing and editing code.
|
||||
|
||||
|
||||
#### Advanced Topics
|
||||
We love to create issues that are good for beginners and label them as `good first issue` or `hacktoberfest`, but some more advanced topics might require extra knowledge. Below is a list of links you can use to learn more about some of the more advance topics that will help you master the Appwrite codebase.
|
||||
|
||||
We love to create issues that are good for beginners and label them as `good first issue` or `hacktoberfest`, but some more advanced topics might require extra knowledge. Below is a list of links you can use to learn more about some of the more advanced topics that will help you master the Appwrite codebase.
|
||||
|
||||
##### Tools and Libs
|
||||
|
||||
- [Docker](https://www.docker.com/get-started)
|
||||
- [PHP FIG](https://www.php-fig.org/) - [PSR-1](https://www.php-fig.org/psr/psr-1/) and [PSR-4](https://www.php-fig.org/psr/psr-4/)
|
||||
- [PHP FIG](https://www.php-fig.org/) - [PSR-12](https://www.php-fig.org/psr/psr-12/)
|
||||
- [PHP Swoole](https://www.swoole.co.uk/)
|
||||
|
||||
Learn more at our [Technology Stack](#technology-stack) section.
|
||||
|
||||
##### Network and Protocols
|
||||
|
||||
- [OSI Model](https://en.wikipedia.org/wiki/OSI_model)
|
||||
- [TCP vs UDP](https://www.guru99.com/tcp-vs-udp-understanding-the-difference.html#:~:text=TCP%20is%20a%20connection%2Doriented,speed%20of%20UDP%20is%20faster&text=TCP%20does%20error%20checking%20and,but%20it%20discards%20erroneous%20packets.)
|
||||
- [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol)
|
||||
@@ -110,11 +137,13 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
||||
- [gRPC](https://en.wikipedia.org/wiki/GRPC)
|
||||
|
||||
##### Architecture
|
||||
|
||||
- [Microservices vs Monolithic](https://www.mulesoft.com/resources/api/microservices-vs-monolithic#:~:text=Microservices%20architecture%20vs%20monolithic%20architecture&text=A%20monolithic%20application%20is%20built%20as%20a%20single%20unit.&text=To%20make%20any%20alterations%20to,formally%20with%20business%2Doriented%20APIs.)
|
||||
- [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) - Appwrite console architecture
|
||||
|
||||
##### Security
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/0.7.x/docs/specs/authentication.drawio.svg)
|
||||
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/master/docs/specs/authentication.drawio.svg)
|
||||
- [OAuth](https://en.wikipedia.org/wiki/OAuth)
|
||||
- [Encryption](https://medium.com/searchencrypt/what-is-encryption-how-does-it-work-e8f20e340537#:~:text=Encryption%20is%20a%20process%20that,%2C%20or%20decrypt%2C%20the%20information.)
|
||||
- [Hashing](https://searchsqlserver.techtarget.com/definition/hashing#:~:text=Hashing%20is%20the%20transformation%20of,it%20using%20the%20original%20value.)
|
||||
@@ -124,8 +153,8 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
||||
Appwrite's current structure is a combination of both [Monolithic](https://en.wikipedia.org/wiki/Monolithic_application) and [Microservice](https://en.wikipedia.org/wiki/Microservices) architectures, but our final goal, as we grow, is to be using only microservices.
|
||||
|
||||
---
|
||||

|
||||
---
|
||||
|
||||
## 
|
||||
|
||||
### File Structure
|
||||
|
||||
@@ -194,7 +223,7 @@ Currently, all of the Appwrite microservices are intended to communicate using t
|
||||
|
||||
## Ports
|
||||
|
||||
Appwrite dev version uses ports 80 and 443 as an entry point to the Appwrite API and console. We also expose multiple ports in the range of 9500-9504 for debugging some of the Appwrite containers on dev mode. If you have any conflicts with the ports running on your system, you can easily replace them by editing Appwrite's docker-compose.yml file and executing `docker-compose up -d` command.
|
||||
Appwrite dev version uses ports 80 and 443 as an entry point to the Appwrite API and console. We also expose multiple ports in the range of 9500-9504 for debugging some of the Appwrite containers on dev mode. If you have any conflicts with the ports running on your system, you can easily replace them by editing Appwrite's docker-compose.yml file and executing `docker compose up -d` command.
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@@ -204,15 +233,15 @@ Appwrite stack is combined from a variety of open-source technologies and tools.
|
||||
|
||||
### Other Technologies
|
||||
|
||||
* Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data)
|
||||
* MariaDB - for database storage and queries
|
||||
* InfluxDB - for managing stats and time-series based data
|
||||
* Statsd - for sending data over UDP protocol (using Telegraf)
|
||||
* ClamAV - for validating and scanning storage files
|
||||
* Imagemagick - for manipulating and managing image media files.
|
||||
* Webp - for better compression of images on supporting clients
|
||||
* SMTP - for sending email messages and alerts
|
||||
* Resque - for managing data queues and scheduled tasks over a Redis server
|
||||
- Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data)
|
||||
- MariaDB - for database storage and queries
|
||||
- InfluxDB - for managing stats and time-series based data
|
||||
- Statsd - for sending data over UDP protocol (using Telegraf)
|
||||
- ClamAV - for validating and scanning storage files
|
||||
- Imagemagick - for manipulating and managing image media files.
|
||||
- Webp - for better compression of images on supporting clients
|
||||
- SMTP - for sending email messages and alerts
|
||||
- Resque - for managing data queues and scheduled tasks over a Redis server
|
||||
|
||||
## Package Managers
|
||||
|
||||
@@ -224,7 +253,7 @@ Appwrite uses [PHP's Composer](https://getcomposer.org/) for managing dependenci
|
||||
|
||||
## Coding Standards
|
||||
|
||||
Appwrite is following the [PHP-FIG standards](https://www.php-fig.org/). Currently, we are using both PSR-0 and PSR-4 for coding standards and autoloading standards. Soon we will also review the project for support with PSR-12 (Extended Coding Style).
|
||||
Appwrite is following the [PHP-FIG standards](https://www.php-fig.org/). Currently, we are using both PSR-0 and PSR-12 for coding standards and autoloading standards.
|
||||
|
||||
We use prettier for our JS coding standards and auto-formatting our code.
|
||||
|
||||
@@ -236,14 +265,14 @@ We wish Appwrite will be as easy to set up and in a single, localhost, and easy
|
||||
|
||||
When contributing code, please take into account the following considerations:
|
||||
|
||||
* Response Time
|
||||
* Throughput
|
||||
* Requests per Seconds
|
||||
* Network Usage
|
||||
* Memory Usage
|
||||
* Browser Rendering
|
||||
* Background Jobs
|
||||
* Task Execution Time
|
||||
- Response Time
|
||||
- Throughput
|
||||
- Requests per Seconds
|
||||
- Network Usage
|
||||
- Memory Usage
|
||||
- Browser Rendering
|
||||
- Background Jobs
|
||||
- Task Execution Time
|
||||
|
||||
## Security & Privacy
|
||||
|
||||
@@ -280,6 +309,7 @@ Before running the command, make sure you have proper write permissions to the A
|
||||
```bash
|
||||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t appwrite/appwrite:dev --push .
|
||||
```
|
||||
|
||||
**Build Functions Runtimes**
|
||||
|
||||
The Runtimes for all supported cloud functions (multicore builds) can be found at the [open-runtimes/open-runtimes](https://github.com/open-runtimes/open-runtimes) repository.
|
||||
@@ -288,7 +318,7 @@ The Runtimes for all supported cloud functions (multicore builds) can be found a
|
||||
|
||||
For generating a new console SDK follow the next steps:
|
||||
|
||||
1. Update the console spec file located at `app/config/specs/swagger2-0.12.x.console.json` from the dynamic version located at `https://localhost/specs/swagger2?platform=console`
|
||||
1. Update the console spec file located at `app/config/specs/swagger2-<version-number>.console.json` using Appwrite Tasks. Run the `php app/cli.php specs <version-number> normal` command in a running `appwrite/appwrite` container.
|
||||
2. Generate a new SDK using the command `php app/cli.php sdks`
|
||||
3. Change your working dir using `cd app/sdks/console-web`
|
||||
4. Build the new SDK `npm run build`
|
||||
@@ -299,29 +329,29 @@ For generating a new console SDK follow the next steps:
|
||||
|
||||
Things to remember when releasing SDKs
|
||||
|
||||
* Update the Changelogs in **docs/sdks** (right now only Dart and Flutter are using these)
|
||||
* Update **GETTING_STARTED.md** in **docs/sdks** for each SDKs if any changes in the related APIs in there
|
||||
* Update SDK versions as required on **app/config/platforms.php**
|
||||
* Generate SDKs using the command `php app/cli.php sdks` and follow the instructions
|
||||
* Release new tags on GitHub repository for each SDKs
|
||||
- Update the Changelogs in **docs/sdks** (right now only Dart and Flutter are using these)
|
||||
- Update **GETTING_STARTED.md** in **docs/sdks** for each SDKs if any changes in the related APIs in there
|
||||
- Update SDK versions as required on **app/config/platforms.php**
|
||||
- Generate SDKs using the command `php app/cli.php sdks` and follow the instructions
|
||||
- Release new tags on GitHub repository for each SDKs
|
||||
|
||||
## Debug
|
||||
|
||||
Appwrite uses [yasd](https://github.com/swoole/yasd) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension or if you are in PHP Storm you don't need any plugin. Below are the settings required for remote debugger connection.
|
||||
|
||||
First, you need to create an init file. Duplicate **dev/yasd_init.php.stub** file and name it **dev/yasd_init.php** and there change the IP address to your development machine's IP. Without the proper IP address debugger wont connect. And you also need to set **DEBUG** build arg in **appwrite** service in **docker-compose.yml** file.
|
||||
First, you need to create an init file. Duplicate **dev/yasd_init.php.stub** file and name it **dev/yasd_init.php** and then change the IP address to your development machine's IP. Without the proper IP address debugger won't connect. And you also need to set **DEBUG** build arg in **appwrite** service in **docker-compose.yml** file.
|
||||
|
||||
### VS Code Launch Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9005,
|
||||
"pathMappings": {
|
||||
"/usr/src/code": "${workspaceRoot}"
|
||||
},
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9005,
|
||||
"pathMappings": {
|
||||
"/usr/src/code": "${workspaceRoot}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -334,85 +364,82 @@ In settings, go to **Languages & Frameworks** > **PHP** > **Debug**, there under
|
||||
To run all tests manually, use the Appwrite Docker CLI from your terminal:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite test
|
||||
docker compose exec appwrite test
|
||||
```
|
||||
|
||||
To run unit tests use:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite test /usr/src/code/tests/unit
|
||||
docker compose exec appwrite test /usr/src/code/tests/unit
|
||||
```
|
||||
|
||||
To run end-2-end tests use:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite test /usr/src/code/tests/e2e
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e
|
||||
```
|
||||
|
||||
To run end-2-end tests for a spcific service use:
|
||||
To run end-2-end tests for a specific service use:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.
|
||||
|
||||
```
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
```
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run --rm skandyla/wrk -t3 -c100 -d30 https://[APPWRITE_HOSTNAME_OR_IP]
|
||||
```
|
||||
|
||||
## Code Maintenance
|
||||
## Code Maintenance
|
||||
|
||||
We use some automation tools to help us keep a healthy codebase.
|
||||
|
||||
Improve PHP execution time by using [fully-qualified function calls](https://veewee.github.io/blog/optimizing-php-performance-by-fq-function-calls/):
|
||||
**Run Formatter:**
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix src/ --rules=native_function_invocation --allow-risky=yes
|
||||
# Run on all files
|
||||
composer format
|
||||
# Run on single file or folder
|
||||
composer format <your file path>
|
||||
```
|
||||
|
||||
Coding Standards:
|
||||
**Run Linter:**
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix app/controllers --rules='{"braces": {"allow_single_line_closure": true}}'
|
||||
```
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix src --rules='{"braces": {"allow_single_line_closure": true}}'
|
||||
```
|
||||
|
||||
Static Code Analysis:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite /usr/src/code/vendor/bin/psalm
|
||||
# Run on all files
|
||||
composer lint
|
||||
# Run on single file or folder
|
||||
composer lint <your file path>
|
||||
```
|
||||
|
||||
## Tutorials
|
||||
|
||||
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:
|
||||
|
||||
* [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
* [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
|
||||
* [Running in Production](./docs/tutorials/running-in-production.md)
|
||||
* [Adding Storage Adapter](./docs/tutorials/add-storage-adapter.md)
|
||||
- [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
- [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
|
||||
- [Running in Production](./docs/tutorials/running-in-production.md)
|
||||
- [Adding Storage Adapter](./docs/tutorials/add-storage-adapter.md)
|
||||
|
||||
## Other Ways to Help
|
||||
|
||||
Pull requests are great, but there are many other areas where you can help Appwrite.
|
||||
Pull requests are great, but there are many other areas where you can help Appwrite.
|
||||
|
||||
### Blogging & Speaking
|
||||
|
||||
@@ -437,4 +464,3 @@ Submitting documentation updates, enhancements, designs, or bug fixes. Spelling
|
||||
### Helping Someone
|
||||
|
||||
Searching for Appwrite on Discord, GitHub, or StackOverflow and helping someone else who needs help. You can also help by teaching others how to contribute to Appwrite's repo!
|
||||
|
||||
|
||||
+38
-177
@@ -14,116 +14,22 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
|
||||
|
||||
FROM node:16.14.2-alpine3.15 as node
|
||||
|
||||
WORKDIR /usr/local/src/
|
||||
COPY app/console /usr/local/src/console
|
||||
|
||||
COPY package-lock.json /usr/local/src/
|
||||
COPY package.json /usr/local/src/
|
||||
COPY gulpfile.js /usr/local/src/
|
||||
COPY public /usr/local/src/public
|
||||
WORKDIR /usr/local/src/console
|
||||
|
||||
ARG VITE_GA_PROJECT
|
||||
ARG VITE_CONSOLE_MODE
|
||||
ARG VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
|
||||
|
||||
ENV VITE_GA_PROJECT=$VITE_GA_PROJECT
|
||||
ENV VITE_CONSOLE_MODE=$VITE_CONSOLE_MODE
|
||||
ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
|
||||
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
|
||||
FROM php:8.0.18-cli-alpine3.15 as compile
|
||||
|
||||
ARG DEBUG=false
|
||||
ENV DEBUG=$DEBUG
|
||||
|
||||
ENV PHP_REDIS_VERSION=5.3.7 \
|
||||
PHP_MONGODB_VERSION=1.13.0 \
|
||||
PHP_SWOOLE_VERSION=v4.8.9 \
|
||||
PHP_IMAGICK_VERSION=3.7.0 \
|
||||
PHP_YAML_VERSION=2.2.2 \
|
||||
PHP_MAXMINDDB_VERSION=v1.11.0
|
||||
|
||||
RUN \
|
||||
apk add --no-cache --virtual .deps \
|
||||
make \
|
||||
automake \
|
||||
autoconf \
|
||||
gcc \
|
||||
g++ \
|
||||
git \
|
||||
zlib-dev \
|
||||
brotli-dev \
|
||||
openssl-dev \
|
||||
yaml-dev \
|
||||
imagemagick \
|
||||
imagemagick-dev \
|
||||
libmaxminddb-dev
|
||||
|
||||
RUN docker-php-ext-install sockets
|
||||
|
||||
FROM compile AS redis
|
||||
RUN \
|
||||
# Redis Extension
|
||||
git clone --depth 1 --branch $PHP_REDIS_VERSION https://github.com/phpredis/phpredis.git && \
|
||||
cd phpredis && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
## Swoole Extension
|
||||
FROM compile AS swoole
|
||||
RUN \
|
||||
git clone --depth 1 --branch $PHP_SWOOLE_VERSION https://github.com/swoole/swoole-src.git && \
|
||||
cd swoole-src && \
|
||||
phpize && \
|
||||
./configure --enable-sockets --enable-http2 --enable-openssl && \
|
||||
make && make install && \
|
||||
cd ..
|
||||
|
||||
## Swoole Debugger setup
|
||||
RUN if [ "$DEBUG" == "true" ]; then \
|
||||
cd /tmp && \
|
||||
apk add boost-dev && \
|
||||
git clone --depth 1 https://github.com/swoole/yasd && \
|
||||
cd yasd && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install && \
|
||||
cd ..;\
|
||||
fi
|
||||
|
||||
## Imagick Extension
|
||||
FROM compile AS imagick
|
||||
RUN \
|
||||
git clone --depth 1 --branch $PHP_IMAGICK_VERSION https://github.com/imagick/imagick && \
|
||||
cd imagick && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
## YAML Extension
|
||||
FROM compile AS yaml
|
||||
RUN \
|
||||
git clone --depth 1 --branch $PHP_YAML_VERSION https://github.com/php/pecl-file_formats-yaml && \
|
||||
cd pecl-file_formats-yaml && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
## Maxminddb extension
|
||||
FROM compile AS maxmind
|
||||
RUN \
|
||||
git clone --depth 1 --branch $PHP_MAXMINDDB_VERSION https://github.com/maxmind/MaxMind-DB-Reader-php.git && \
|
||||
cd MaxMind-DB-Reader-php && \
|
||||
cd ext && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
# Mongodb Extension
|
||||
FROM compile as mongodb
|
||||
RUN \
|
||||
git clone --depth 1 --branch $PHP_MONGODB_VERSION https://github.com/mongodb/mongo-php-driver.git && \
|
||||
cd mongo-php-driver && \
|
||||
git submodule update --init && \
|
||||
phpize && \
|
||||
./configure && \
|
||||
make && make install
|
||||
|
||||
FROM php:8.0.18-cli-alpine3.15 as final
|
||||
FROM appwrite/base:0.2.2 as final
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
@@ -131,21 +37,17 @@ ARG VERSION=dev
|
||||
ARG DEBUG=false
|
||||
ENV DEBUG=$DEBUG
|
||||
|
||||
ENV DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
|
||||
ENV DOCKER_COMPOSE_VERSION=v2.5.0
|
||||
|
||||
ENV _APP_SERVER=swoole \
|
||||
_APP_ENV=production \
|
||||
_APP_LOCALE=en \
|
||||
_APP_WORKER_PER_CORE= \
|
||||
_APP_DOMAIN=localhost \
|
||||
_APP_DOMAIN_TARGET=localhost \
|
||||
_APP_HOME=https://appwrite.io \
|
||||
_APP_EDITION=community \
|
||||
_APP_CONSOLE_WHITELIST_ROOT=enabled \
|
||||
_APP_CONSOLE_WHITELIST_EMAILS= \
|
||||
_APP_CONSOLE_WHITELIST_IPS= \
|
||||
_APP_SYSTEM_EMAIL_NAME= \
|
||||
_APP_SYSTEM_EMAIL_ADDRESS= \
|
||||
_APP_SYSTEM_RESPONSE_FORMAT= \
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS= \
|
||||
_APP_OPTIONS_ABUSE=enabled \
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled \
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key \
|
||||
@@ -153,15 +55,6 @@ ENV _APP_SERVER=swoole \
|
||||
_APP_STORAGE_ANTIVIRUS=enabled \
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav \
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310 \
|
||||
_APP_STORAGE_DEVICE=Local \
|
||||
_APP_STORAGE_S3_ACCESS_KEY= \
|
||||
_APP_STORAGE_S3_SECRET= \
|
||||
_APP_STORAGE_S3_REGION= \
|
||||
_APP_STORAGE_S3_BUCKET= \
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY= \
|
||||
_APP_STORAGE_DO_SPACES_SECRET= \
|
||||
_APP_STORAGE_DO_SPACES_REGION= \
|
||||
_APP_STORAGE_DO_SPACES_BUCKET= \
|
||||
_APP_REDIS_HOST=redis \
|
||||
_APP_REDIS_PORT=6379 \
|
||||
_APP_DB_HOST=mariadb \
|
||||
@@ -173,20 +66,12 @@ ENV _APP_SERVER=swoole \
|
||||
_APP_INFLUXDB_PORT=8086 \
|
||||
_APP_STATSD_HOST=telegraf \
|
||||
_APP_STATSD_PORT=8125 \
|
||||
_APP_SMTP_HOST= \
|
||||
_APP_SMTP_PORT= \
|
||||
_APP_SMTP_SECURE= \
|
||||
_APP_SMTP_USERNAME= \
|
||||
_APP_SMTP_PASSWORD= \
|
||||
_APP_FUNCTIONS_SIZE_LIMIT=30000000 \
|
||||
_APP_FUNCTIONS_TIMEOUT=900 \
|
||||
_APP_FUNCTIONS_CONTAINERS=10 \
|
||||
_APP_FUNCTIONS_CPUS=1 \
|
||||
_APP_FUNCTIONS_MEMORY=128 \
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=128 \
|
||||
_APP_EXECUTOR_SECRET=a-random-secret \
|
||||
_APP_EXECUTOR_HOST=http://appwrite-executor/v1 \
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes \
|
||||
_APP_EXECUTOR_HOST=http://exc1/v1 \
|
||||
_APP_SETUP=self-hosted \
|
||||
_APP_VERSION=$VERSION \
|
||||
_APP_USAGE_STATS=enabled \
|
||||
@@ -195,36 +80,9 @@ ENV _APP_SERVER=swoole \
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT=1209600 \
|
||||
# 1 Day = 86400 s
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400 \
|
||||
_APP_MAINTENANCE_INTERVAL=86400 \
|
||||
_APP_LOGGING_PROVIDER= \
|
||||
_APP_LOGGING_CONFIG=
|
||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 \
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
RUN \
|
||||
apk update \
|
||||
&& apk add --no-cache --virtual .deps \
|
||||
make \
|
||||
automake \
|
||||
autoconf \
|
||||
gcc \
|
||||
g++ \
|
||||
curl-dev \
|
||||
&& apk add --no-cache \
|
||||
libstdc++ \
|
||||
certbot \
|
||||
brotli-dev \
|
||||
yaml-dev \
|
||||
imagemagick \
|
||||
imagemagick-dev \
|
||||
libmaxminddb-dev \
|
||||
certbot \
|
||||
docker-cli \
|
||||
docker-compose \
|
||||
libgomp \
|
||||
&& docker-php-ext-install sockets opcache pdo_mysql \
|
||||
&& apk del .deps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN \
|
||||
if [ "$DEBUG" == "true" ]; then \
|
||||
@@ -234,20 +92,13 @@ RUN \
|
||||
WORKDIR /usr/src/code
|
||||
|
||||
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
|
||||
COPY --from=node /usr/local/src/public/dist /usr/src/code/public/dist
|
||||
COPY --from=swoole /usr/local/lib/php/extensions/no-debug-non-zts-20200930/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/yasd.so* /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=redis /usr/local/lib/php/extensions/no-debug-non-zts-20200930/redis.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=imagick /usr/local/lib/php/extensions/no-debug-non-zts-20200930/imagick.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=yaml /usr/local/lib/php/extensions/no-debug-non-zts-20200930/yaml.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=maxmind /usr/local/lib/php/extensions/no-debug-non-zts-20200930/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20200930/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20200930/
|
||||
COPY --from=node /usr/local/src/console/build /usr/src/code/console
|
||||
|
||||
# Add Source Code
|
||||
COPY ./app /usr/src/code/app
|
||||
COPY ./public /usr/src/code/public
|
||||
COPY ./bin /usr/local/bin
|
||||
COPY ./docs /usr/src/code/docs
|
||||
COPY ./public/fonts /usr/src/code/public/fonts
|
||||
COPY ./public/images /usr/src/code/public/images
|
||||
COPY ./src /usr/src/code/src
|
||||
|
||||
# Set Volumes
|
||||
@@ -266,36 +117,40 @@ RUN mkdir -p /storage/uploads && \
|
||||
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
|
||||
chmod +x /usr/local/bin/clear-card-cache && \
|
||||
chmod +x /usr/local/bin/calc-users-stats && \
|
||||
chmod +x /usr/local/bin/calc-tier-stats && \
|
||||
chmod +x /usr/local/bin/patch-delete-project-collections && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/db-backup && \
|
||||
chmod +x /usr/local/bin/db-restore && \
|
||||
chmod +x /usr/local/bin/volume-sync && \
|
||||
chmod +x /usr/local/bin/usage && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
chmod +x /usr/local/bin/migrate && \
|
||||
chmod +x /usr/local/bin/realtime && \
|
||||
chmod +x /usr/local/bin/executor && \
|
||||
chmod +x /usr/local/bin/schedule && \
|
||||
chmod +x /usr/local/bin/sdks && \
|
||||
chmod +x /usr/local/bin/specs && \
|
||||
chmod +x /usr/local/bin/ssl && \
|
||||
chmod +x /usr/local/bin/hamster && \
|
||||
chmod +x /usr/local/bin/test && \
|
||||
chmod +x /usr/local/bin/vars && \
|
||||
chmod +x /usr/local/bin/worker-audits && \
|
||||
chmod +x /usr/local/bin/worker-certificates && \
|
||||
chmod +x /usr/local/bin/worker-database && \
|
||||
chmod +x /usr/local/bin/worker-databases && \
|
||||
chmod +x /usr/local/bin/worker-deletes && \
|
||||
chmod +x /usr/local/bin/worker-functions && \
|
||||
chmod +x /usr/local/bin/worker-builds && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-messaging && \
|
||||
chmod +x /usr/local/bin/worker-webhooks
|
||||
|
||||
# Letsencrypt Permissions
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
# Enable Extensions
|
||||
RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini
|
||||
RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini
|
||||
RUN echo extension=imagick.so >> /usr/local/etc/php/conf.d/imagick.ini
|
||||
RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini
|
||||
RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini
|
||||
RUN if [ "$DEBUG" == "true" ]; then printf "zend_extension=yasd \nyasd.debug_mode=remote \nyasd.init_file=/usr/local/dev/yasd_init.php \nyasd.remote_port=9005 \nyasd.log_level=-1" >> /usr/local/etc/php/conf.d/yasd.ini; fi
|
||||
|
||||
RUN if [ "$DEBUG" == "true" ]; then echo "opcache.enable=0" >> /usr/local/etc/php/conf.d/appwrite.ini; fi
|
||||
@@ -306,6 +161,12 @@ RUN echo "default_socket_timeout=-1" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit=1235" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
|
||||
RUN \
|
||||
apk update \
|
||||
&& apk add --no-cache \
|
||||
docker \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD [ "php", "app/http.php", "-dopcache.preload=opcache.preload=/usr/src/code/app/preload.php" ]
|
||||
CMD [ "php", "app/http.php", "-dopcache.preload=opcache.preload=/usr/src/code/app/preload.php" ]
|
||||
+16
-11
@@ -8,16 +8,21 @@
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<!-- [](https://hacktoberfest.appwrite.io) -->
|
||||
<!-- [](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[](https://hacktoberfest.appwrite.io)
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[](https://travis-ci.com/appwrite/appwrite)
|
||||
[](https://github.com/appwrite/appwrite/actions)
|
||||
[](https://twitter.com/appwrite)
|
||||
[](docs/tutorials/add-translations.md)
|
||||
[](https://store.appwrite.io)
|
||||
|
||||
<!-- [](https://hub.docker.com/r/appwrite/appwrite) -->
|
||||
<!-- [](docs/tutorials/add-translations.md) -->
|
||||
<!-- [](https://store.appwrite.io) -->
|
||||
|
||||
[English](README.md) | 简体中文
|
||||
|
||||
[**我们发布了 Appwrite Console 2.0 版本,点击这里了解更多!**](https://medium.com/appwrite-io/announcing-console-2-0-2e0e96891cb0?source=friends_link&sk=7a82b4069778e3adc165dc026e960fe1)
|
||||
|
||||
Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面极简了从零编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
|
||||
|
||||
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存,图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
|
||||
@@ -59,7 +64,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:0.14.1
|
||||
appwrite/appwrite:1.1.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -71,7 +76,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:0.14.1
|
||||
appwrite/appwrite:1.1.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -81,13 +86,13 @@ 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:0.14.1
|
||||
appwrite/appwrite:1.1.2
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
|
||||
需要自定义容器构架,请查看我们的 Docker [环境变量](https://appwrite.io/docs/environment-variables) 文档。您还可以参考我们的 [docker-compose.yml](https://gist.github.com/eldadfux/977869ff6bdd7312adfd4e629ee15cc5#file-docker-compose-yml) 文件手动设置环境。
|
||||
需要自定义容器构架,请查看我们的 Docker [环境变量](https://appwrite.io/docs/environment-variables) 文档。您还可以参考我们的 [docker-compose.yml](https://appwrite.io/install/compose) 和 [.env](https://appwrite.io/install/env) 文件手动设置环境。
|
||||
|
||||
### 从旧版本升级
|
||||
|
||||
@@ -109,7 +114,7 @@ docker run -it --rm ,
|
||||
* [**帐户**](https://appwrite.io/docs/client/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
|
||||
* [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
|
||||
* [**团队**](https://appwrite.io/docs/client/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
|
||||
* [**数据库**](https://appwrite.io/docs/client/database) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
|
||||
* [**数据库**](https://appwrite.io/docs/client/databases) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
|
||||
* [**贮存**](https://appwrite.io/docs/client/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
|
||||
* [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
|
||||
* [**语言适配**](https://appwrite.io/docs/client/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
|
||||
@@ -128,7 +133,7 @@ docker run -it --rm ,
|
||||
|
||||
#### 服务器
|
||||
* ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
|
||||
* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwr实验 团队维护)
|
||||
* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
|
||||
* ✅ [Dart](https://github.com/appwrite/sdk-for-dart) - (由 Appwrite 团队维护)
|
||||
* ✅ [Deno](https://github.com/appwrite/sdk-for-deno) - **公测** (由 Appwrite 团队维护)
|
||||
* ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
> It's going to get cloudy! 🌩 ☂️
|
||||
> The Appwrite Cloud is coming soon! You can learn more about our upcoming hosted solution and signup for free credits at: https://appwrite.io/cloud
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://appwrite.io" target="_blank"><img width="260" height="39" src="https://appwrite.io/images/appwrite.svg" alt="Appwrite Logo"></a>
|
||||
@@ -8,23 +11,33 @@
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<!-- [](https://hacktoberfest.appwrite.io) -->
|
||||
|
||||
<!-- [](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[](https://hacktoberfest.appwrite.io)
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[](https://github.com/appwrite/appwrite/actions)
|
||||
[](https://twitter.com/appwrite)
|
||||
[](docs/tutorials/add-translations.md)
|
||||
[](https://store.appwrite.io)
|
||||
|
||||
<!-- [](https://hub.docker.com/r/appwrite/appwrite) -->
|
||||
<!-- [](docs/tutorials/add-translations.md) -->
|
||||
<!-- [](https://store.appwrite.io) -->
|
||||
|
||||
English | [简体中文](README-CN.md)
|
||||
|
||||
[**Appwrite 0.14 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-014-with-11-cloud-function-runtimes-36f5)
|
||||
[**Appwrite Console 2.0 has been released! Learn what's new!**](https://medium.com/appwrite-io/announcing-console-2-0-2e0e96891cb0?source=friends_link&sk=7a82b4069778e3adc165dc026e960fe1)
|
||||
|
||||
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
|
||||
|
||||
Using Appwrite, you can easily integrate your app with user authentication & multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs).
|
||||
|
||||
<p align="center">
|
||||
<br />
|
||||
<a href="https://www.producthunt.com/posts/appwrite-2?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-appwrite-2" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=360315&theme=light&period=daily" alt="Appwrite - 100% open source alternative for Firebase | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
Find out more at: [https://appwrite.io](https://appwrite.io)
|
||||
@@ -62,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:0.14.1
|
||||
appwrite/appwrite:1.1.2
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -74,49 +87,72 @@ 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:0.14.1
|
||||
appwrite/appwrite:1.1.2
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
||||
```powershell
|
||||
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:0.14.1
|
||||
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.1.2
|
||||
```
|
||||
|
||||
Once the Docker installation completes, 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 installation completes.
|
||||
|
||||
|
||||
For advanced production and custom installation, check out our Docker [environment variables](https://appwrite.io/docs/environment-variables) docs. You can also use our public [docker-compose.yml](https://gist.github.com/eldadfux/977869ff6bdd7312adfd4e629ee15cc5#file-docker-compose-yml) file to manually set up an environment.
|
||||
For advanced production and custom installation, check out our Docker [environment variables](https://appwrite.io/docs/environment-variables) docs. You can also use our public [docker-compose.yml](https://appwrite.io/install/compose) and [.env](https://appwrite.io/install/env) files to manually set up an environment.
|
||||
|
||||
### Upgrade from an Older Version
|
||||
|
||||
If you are upgrading your Appwrite server from an older version, you should use the Appwrite migration tool once your setup is completed. For more information regarding this, check out the [Installation Docs](https://appwrite.io/docs/installation).
|
||||
|
||||
## One-Click Setups
|
||||
|
||||
In addition to running Appwrite locally, you can also launch Appwrite using a pre-configured setup. This allows you to get up and running with Appwrite quickly without installing Docker on your local machine.
|
||||
|
||||
Choose from one of the providers below:
|
||||
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td align="center" width="100" height="100">
|
||||
<a href="https://marketplace.digitalocean.com/apps/appwrite">
|
||||
<img width="50" height="39" src="public/images/integrations/digitalocean-logo.svg" alt="DigitalOcean Logo" />
|
||||
<br /><sub><b>DigitalOcean</b></sub></a>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100" height="100">
|
||||
<a href="https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod">
|
||||
<img width="50" height="39" src="public/images/integrations/gitpod-logo.svg" alt="Gitpod Logo" />
|
||||
<br /><sub><b>Gitpod</b></sub></a>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Getting Started
|
||||
|
||||
Getting started with Appwrite is as easy as creating a new project, choosing your platform, and integrating its SDK into your code. You can easily get started with your platform of choice by reading one of our Getting Started tutorials.
|
||||
|
||||
* [Getting Started for Web](https://appwrite.io/docs/getting-started-for-web)
|
||||
* [Getting Started for Flutter](https://appwrite.io/docs/getting-started-for-flutter)
|
||||
* [Getting Started for Apple](https://appwrite.io/docs/getting-started-for-apple)
|
||||
* [Getting Started for Android](https://appwrite.io/docs/getting-started-for-android)
|
||||
* [Getting Started for Server](https://appwrite.io/docs/getting-started-for-server)
|
||||
* [Getting Started for CLI](https://appwrite.io/docs/command-line)
|
||||
- [Getting Started for Web](https://appwrite.io/docs/getting-started-for-web)
|
||||
- [Getting Started for Flutter](https://appwrite.io/docs/getting-started-for-flutter)
|
||||
- [Getting Started for Apple](https://appwrite.io/docs/getting-started-for-apple)
|
||||
- [Getting Started for Android](https://appwrite.io/docs/getting-started-for-android)
|
||||
- [Getting Started for Server](https://appwrite.io/docs/getting-started-for-server)
|
||||
- [Getting Started for CLI](https://appwrite.io/docs/command-line)
|
||||
|
||||
### Services
|
||||
|
||||
* [**Account**](https://appwrite.io/docs/client/account) - Manage current user authentication and account. Track and manage the user sessions, devices, sign-in methods, and security logs.
|
||||
* [**Users**](https://appwrite.io/docs/server/users) - Manage and list all project users when in admin mode.
|
||||
* [**Teams**](https://appwrite.io/docs/client/teams) - Manage and group users in teams. Manage memberships, invites, and user roles within a team.
|
||||
* [**Database**](https://appwrite.io/docs/client/database) - Manage database collections and documents. Read, create, update, and delete documents and filter lists of document collections using advanced filters.
|
||||
* [**Storage**](https://appwrite.io/docs/client/storage) - Manage storage files. Read, create, delete, and preview files. Manipulate the preview of your files to fit your app perfectly. All files are scanned by ClamAV and stored in a secure and encrypted way.
|
||||
* [**Functions**](https://appwrite.io/docs/server/functions) - Customize your Appwrite server by executing your custom code in a secure, isolated environment. You can trigger your code on any Appwrite system event, manually or using a CRON schedule.
|
||||
* [**Locale**](https://appwrite.io/docs/client/locale) - Track your user's location, and manage your app locale-based data.
|
||||
* [**Avatars**](https://appwrite.io/docs/client/avatars) - Manage your users' avatars, countries' flags, browser icons, credit card symbols, and generate QR codes.
|
||||
- [**Account**](https://appwrite.io/docs/client/account) - Manage current user authentication and account. Track and manage the user sessions, devices, sign-in methods, and security logs.
|
||||
- [**Users**](https://appwrite.io/docs/server/users) - Manage and list all project users when building backend integrations with Server SDKs.
|
||||
- [**Teams**](https://appwrite.io/docs/client/teams) - Manage and group users in teams. Manage memberships, invites, and user roles within a team.
|
||||
- [**Databases**](https://appwrite.io/docs/client/databases) - Manage databases, collections and documents. Read, create, update, and delete documents and filter lists of document collections using advanced filters.
|
||||
- [**Storage**](https://appwrite.io/docs/client/storage) - Manage storage files. Read, create, delete, and preview files. Manipulate the preview of your files to fit your app perfectly. All files are scanned by ClamAV and stored in a secure and encrypted way.
|
||||
- [**Functions**](https://appwrite.io/docs/server/functions) - Customize your Appwrite server by executing your custom code in a secure, isolated environment. You can trigger your code on any Appwrite system event, manually or using a CRON schedule.
|
||||
- [**Realtime**](https://appwrite.io/docs/realtime) - Listen to real-time events for any of your Appwrite services including users, storage, functions, databases and more.
|
||||
- [**Locale**](https://appwrite.io/docs/client/locale) - Track your user's location, and manage your app locale-based data.
|
||||
- [**Avatars**](https://appwrite.io/docs/client/avatars) - Manage your users' avatars, countries' flags, browser icons, credit card symbols, and generate QR codes.
|
||||
|
||||
For the complete API documentation, visit [https://appwrite.io/docs](https://appwrite.io/docs). For more tutorials, news and announcements check out our [blog](https://medium.com/appwrite-io) and [Discord Server](https://discord.gg/GSeTUeA).
|
||||
|
||||
@@ -125,34 +161,36 @@ For the complete API documentation, visit [https://appwrite.io/docs](https://app
|
||||
Below is a list of currently supported platforms and languages. If you wish to help us add support to your platform of choice, you can go over to our [SDK Generator](https://github.com/appwrite/sdk-generator) project and view our [contribution guide](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md).
|
||||
|
||||
#### Client
|
||||
* ✅ [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team)
|
||||
* ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team)
|
||||
* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **Beta** (Maintained by the Appwrite Team)
|
||||
* ✅ [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team)
|
||||
|
||||
- ✅ [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team)
|
||||
- ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team)
|
||||
- ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **Beta** (Maintained by the Appwrite Team)
|
||||
- ✅ [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team)
|
||||
|
||||
#### Server
|
||||
* ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team)
|
||||
* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team)
|
||||
* ✅ [Dart](https://github.com/appwrite/sdk-for-dart) - (Maintained by the Appwrite Team)
|
||||
* ✅ [Deno](https://github.com/appwrite/sdk-for-deno) - **Beta** (Maintained by the Appwrite Team)
|
||||
* ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team)
|
||||
* ✅ [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team)
|
||||
* ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) - **Beta** (Maintained by the Appwrite Team)
|
||||
* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **Beta** (Maintained by the Appwrite Team)
|
||||
* ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Experimental** (Maintained by the Appwrite Team)
|
||||
|
||||
- ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team)
|
||||
- ✅ [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team)
|
||||
- ✅ [Dart](https://github.com/appwrite/sdk-for-dart) - (Maintained by the Appwrite Team)
|
||||
- ✅ [Deno](https://github.com/appwrite/sdk-for-deno) - **Beta** (Maintained by the Appwrite Team)
|
||||
- ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team)
|
||||
- ✅ [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team)
|
||||
- ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) - **Beta** (Maintained by the Appwrite Team)
|
||||
- ✅ [Apple](https://github.com/appwrite/sdk-for-apple) - **Beta** (Maintained by the Appwrite Team)
|
||||
- ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Experimental** (Maintained by the Appwrite Team)
|
||||
|
||||
#### Community
|
||||
* ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/))
|
||||
* ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub))
|
||||
|
||||
- ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/))
|
||||
- ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub))
|
||||
|
||||
Looking for more SDKs? - Help us by contributing a pull request to our [SDK Generator](https://github.com/appwrite/sdk-generator)!
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
||||
Appwrite uses a microservices architecture that was designed for easy scaling and delegation of responsibilities. In addition, Appwrite supports multiple APIs (REST, WebSocket, and GraphQL-soon) to allow you to interact with your resources leveraging your existing knowledge and protocols of choice.
|
||||
Appwrite uses a microservices architecture that was designed for easy scaling and delegation of responsibilities. In addition, Appwrite supports multiple APIs (REST, WebSocket, and GraphQL-soon) to allow you to interact with your resources by leveraging your existing knowledge and protocols of choice.
|
||||
|
||||
The Appwrite API layer was designed to be extremely fast by leveraging in-memory caching and delegating any heavy-lifting tasks to the Appwrite background workers. The background workers also allow you to precisely control your compute capacity and costs using a message queue to handle the load. You can learn more about our architecture in the [contribution guide](CONTRIBUTING.md#architecture-1).
|
||||
|
||||
@@ -168,7 +206,7 @@ For security issues, kindly email us at [security@appwrite.io](mailto:security@a
|
||||
|
||||
## Follow Us
|
||||
|
||||
Join our growing community around the world! See our official [Blog](https://medium.com/appwrite-io). Follow us on [Twitter](https://twitter.com/appwrite), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/) , [Dev Community](https://dev.to/appwrite) or join our live [Discord server](https://discord.gg/GSeTUeA) for more help, ideas, and discussions.
|
||||
Join our growing community around the world! See our official [Blog](https://medium.com/appwrite-io). Follow us on [Twitter](https://twitter.com/appwrite), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/), [Dev Community](https://dev.to/appwrite) or join our live [Discord server](https://discord.gg/GSeTUeA) for more help, ideas, and discussions.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+4
-3
@@ -4,9 +4,10 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| <= 0.10 | :x: |
|
||||
| 0.11.x | :white_check_mark: |
|
||||
| 0.12.x | :white_check_mark: |
|
||||
| <= 0.14 | :x: |
|
||||
| 1.15.x | :white_check_mark: |
|
||||
| 1.0.x | :white_check_mark: |
|
||||
| 1.1.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
Binary file not shown.
+183
-19
@@ -1,31 +1,195 @@
|
||||
<?php
|
||||
require_once __DIR__.'/init.php';
|
||||
require_once __DIR__.'/controllers/general.php';
|
||||
|
||||
use Utopia\App;
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Utopia\CLI\CLI;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
$cli = new CLI();
|
||||
CLI::setResource('register', fn()=>$register);
|
||||
|
||||
include 'tasks/doctor.php';
|
||||
include 'tasks/maintenance.php';
|
||||
include 'tasks/install.php';
|
||||
include 'tasks/migrate.php';
|
||||
include 'tasks/sdks.php';
|
||||
include 'tasks/specs.php';
|
||||
include 'tasks/ssl.php';
|
||||
include 'tasks/vars.php';
|
||||
include 'tasks/usage.php';
|
||||
CLI::setResource('cache', function ($pools) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource()
|
||||
;
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
}, ['pools']);
|
||||
|
||||
CLI::setResource('pools', function (Registry $register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
CLI::setResource('dbForConsole', function ($pools, $cache) {
|
||||
$sleep = 3;
|
||||
$maxAttempts = 5;
|
||||
$attempts = 0;
|
||||
$ready = false;
|
||||
|
||||
do {
|
||||
$attempts++;
|
||||
try {
|
||||
// Prepare database connection
|
||||
$dbAdapter = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$dbForConsole = new Database($dbAdapter, $cache);
|
||||
$dbForConsole->setNamespace('console');
|
||||
|
||||
// Ensure tables exist
|
||||
$collections = Config::getParam('collections', [])['console'];
|
||||
$last = \array_key_last($collections);
|
||||
|
||||
if (!($dbForConsole->exists($dbForConsole->getDefaultDatabase(), $last))) { /** TODO cache ready variable using registry */
|
||||
throw new Exception('Tables not ready yet.');
|
||||
}
|
||||
|
||||
$ready = true;
|
||||
} catch (\Exception $err) {
|
||||
Console::warning($err->getMessage());
|
||||
$pools->get('console')->reclaim();
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $maxAttempts);
|
||||
|
||||
if (!$ready) {
|
||||
throw new Exception("Console is not ready yet. Please try again later.");
|
||||
}
|
||||
|
||||
return $dbForConsole;
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
|
||||
$getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForConsole;
|
||||
}
|
||||
|
||||
$databaseName = $project->getAttribute('database');
|
||||
|
||||
if (isset($databases[$databaseName])) {
|
||||
$database = $databases[$databaseName];
|
||||
$database->setNamespace('_' . $project->getInternalId());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($databaseName)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
|
||||
$databases[$databaseName] = $database;
|
||||
|
||||
$database->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
return $database;
|
||||
};
|
||||
|
||||
return $getProjectDB;
|
||||
}, ['pools', 'dbForConsole', 'cache']);
|
||||
|
||||
CLI::setResource('influxdb', function (Registry $register) {
|
||||
$client = $register->get('influxdb'); /** @var InfluxDB\Client $client */
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
do { // check if telegraf database is ready
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if (in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
return $database;
|
||||
}, ['register']);
|
||||
|
||||
CLI::setResource('queueForFunctions', function (Group $pools) {
|
||||
return new Func($pools->get('queue')->pop()->getResource());
|
||||
}, ['pools']);
|
||||
|
||||
CLI::setResource('logError', function (Registry $register) {
|
||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace($namespace);
|
||||
$log->setServer(\gethostname());
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = App::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);
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
};
|
||||
}, ['register']);
|
||||
|
||||
$platform = new Appwrite();
|
||||
$platform->init(Service::TYPE_CLI);
|
||||
|
||||
$cli = $platform->getCli();
|
||||
|
||||
$cli
|
||||
->task('version')
|
||||
->desc('Get the server version')
|
||||
->action(function () {
|
||||
Console::log(App::getEnv('_APP_VERSION', 'UNKNOWN'));
|
||||
->error()
|
||||
->inject('error')
|
||||
->action(function (Throwable $error) {
|
||||
Console::error($error->getMessage());
|
||||
});
|
||||
|
||||
$cli->run();
|
||||
$cli->run();
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
// Auth methods
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
'name' => 'Email/Password',
|
||||
'key' => 'emailPassword',
|
||||
'icon' => '/images/users/email.png',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateSession',
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateEmailSession',
|
||||
'enabled' => true,
|
||||
],
|
||||
'magic-url' => [
|
||||
@@ -42,7 +42,7 @@ return [
|
||||
'name' => 'Phone',
|
||||
'key' => 'phone',
|
||||
'icon' => '/images/users/phone.png',
|
||||
'docs' => '',
|
||||
'enabled' => false,
|
||||
'docs' => 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreatePhoneSession',
|
||||
'enabled' => true,
|
||||
],
|
||||
];
|
||||
];
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
|
||||
return [
|
||||
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
|
||||
'aa' => __DIR__.'/browsers/avant.png',
|
||||
'an' => __DIR__.'/browsers/android-webview-beta.png',
|
||||
'ch' => __DIR__.'/browsers/chrome.png',
|
||||
'ci' => __DIR__.'/browsers/chrome.png', //Chrome Mobile iOS
|
||||
'cm' => __DIR__.'/browsers/chrome.png', //Chrome Mobile
|
||||
'cr' => __DIR__.'/browsers/chromium.png',
|
||||
'ff' => __DIR__.'/browsers/firefox.png',
|
||||
'sf' => __DIR__.'/browsers/safari.png',
|
||||
'mf' => __DIR__.'/browsers/safari.png',
|
||||
'ps' => __DIR__.'/browsers/edge.png',
|
||||
'oi' => __DIR__.'/browsers/edge.png',
|
||||
'om' => __DIR__.'/browsers/opera-mini.png',
|
||||
'op' => __DIR__.'/browsers/opera.png',
|
||||
'on' => __DIR__.'/browsers/opera.png',
|
||||
'aa' => __DIR__ . '/browsers/avant.png',
|
||||
'an' => __DIR__ . '/browsers/android-webview-beta.png',
|
||||
'ch' => __DIR__ . '/browsers/chrome.png',
|
||||
'ci' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile iOS
|
||||
'cm' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile
|
||||
'cr' => __DIR__ . '/browsers/chromium.png',
|
||||
'ff' => __DIR__ . '/browsers/firefox.png',
|
||||
'sf' => __DIR__ . '/browsers/safari.png',
|
||||
'mf' => __DIR__ . '/browsers/safari.png',
|
||||
'ps' => __DIR__ . '/browsers/edge.png',
|
||||
'oi' => __DIR__ . '/browsers/edge.png',
|
||||
'om' => __DIR__ . '/browsers/opera-mini.png',
|
||||
'op' => __DIR__ . '/browsers/opera.png',
|
||||
'on' => __DIR__ . '/browsers/opera.png',
|
||||
|
||||
/*
|
||||
'36' => '360 Phone Browser',
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'amex' => __DIR__.'/credit-cards/amex.png',
|
||||
'argencard' => __DIR__.'/credit-cards/argencard.png',
|
||||
'cabal' => __DIR__.'/credit-cards/cabal.png',
|
||||
'censosud' => __DIR__.'/credit-cards/consosud.png',
|
||||
'diners' => __DIR__.'/credit-cards/diners.png',
|
||||
'discover' => __DIR__.'/credit-cards/discover.png',
|
||||
'elo' => __DIR__.'/credit-cards/elo.png',
|
||||
'hipercard' => __DIR__.'/credit-cards/hipercard.png',
|
||||
'jcb' => __DIR__.'/credit-cards/jcb.png',
|
||||
'mastercard' => __DIR__.'/credit-cards/mastercard.png',
|
||||
'naranja' => __DIR__.'/credit-cards/naranja.png',
|
||||
'targeta-shopping' => __DIR__.'/credit-cards/tarjeta-shopping.png',
|
||||
'union-china-pay' => __DIR__.'/credit-cards/union-china-pay.png',
|
||||
'visa' => __DIR__.'/credit-cards/visa.png',
|
||||
'mir' => __DIR__.'/credit-cards/mir.png',
|
||||
'maestro' => __DIR__.'/credit-cards/maestro.png',
|
||||
'amex' => __DIR__ . '/credit-cards/amex.png',
|
||||
'argencard' => __DIR__ . '/credit-cards/argencard.png',
|
||||
'cabal' => __DIR__ . '/credit-cards/cabal.png',
|
||||
'censosud' => __DIR__ . '/credit-cards/consosud.png',
|
||||
'diners' => __DIR__ . '/credit-cards/diners.png',
|
||||
'discover' => __DIR__ . '/credit-cards/discover.png',
|
||||
'elo' => __DIR__ . '/credit-cards/elo.png',
|
||||
'hipercard' => __DIR__ . '/credit-cards/hipercard.png',
|
||||
'jcb' => __DIR__ . '/credit-cards/jcb.png',
|
||||
'mastercard' => __DIR__ . '/credit-cards/mastercard.png',
|
||||
'naranja' => __DIR__ . '/credit-cards/naranja.png',
|
||||
'targeta-shopping' => __DIR__ . '/credit-cards/tarjeta-shopping.png',
|
||||
'union-china-pay' => __DIR__ . '/credit-cards/union-china-pay.png',
|
||||
'visa' => __DIR__ . '/credit-cards/visa.png',
|
||||
'mir' => __DIR__ . '/credit-cards/mir.png',
|
||||
'maestro' => __DIR__ . '/credit-cards/maestro.png',
|
||||
];
|
||||
|
||||
+194
-194
@@ -1,198 +1,198 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'af' => __DIR__.'/flags/af.png',
|
||||
'ao' => __DIR__.'/flags/ao.png',
|
||||
'al' => __DIR__.'/flags/al.png',
|
||||
'ad' => __DIR__.'/flags/ad.png',
|
||||
'ae' => __DIR__.'/flags/ae.png',
|
||||
'ar' => __DIR__.'/flags/ar.png',
|
||||
'am' => __DIR__.'/flags/am.png',
|
||||
'ag' => __DIR__.'/flags/ag.png',
|
||||
'au' => __DIR__.'/flags/au.png',
|
||||
'at' => __DIR__.'/flags/at.png',
|
||||
'az' => __DIR__.'/flags/az.png',
|
||||
'bi' => __DIR__.'/flags/bi.png',
|
||||
'be' => __DIR__.'/flags/be.png',
|
||||
'bj' => __DIR__.'/flags/bj.png',
|
||||
'bf' => __DIR__.'/flags/bf.png',
|
||||
'bd' => __DIR__.'/flags/bd.png',
|
||||
'bg' => __DIR__.'/flags/bg.png',
|
||||
'bh' => __DIR__.'/flags/bh.png',
|
||||
'bs' => __DIR__.'/flags/bs.png',
|
||||
'ba' => __DIR__.'/flags/ba.png',
|
||||
'by' => __DIR__.'/flags/by.png',
|
||||
'bz' => __DIR__.'/flags/bz.png',
|
||||
'bo' => __DIR__.'/flags/bo.png',
|
||||
'br' => __DIR__.'/flags/br.png',
|
||||
'bb' => __DIR__.'/flags/bb.png',
|
||||
'bn' => __DIR__.'/flags/bn.png',
|
||||
'bt' => __DIR__.'/flags/bt.png',
|
||||
'bw' => __DIR__.'/flags/bw.png',
|
||||
'cf' => __DIR__.'/flags/cf.png',
|
||||
'ca' => __DIR__.'/flags/ca.png',
|
||||
'ch' => __DIR__.'/flags/ch.png',
|
||||
'cl' => __DIR__.'/flags/cl.png',
|
||||
'cn' => __DIR__.'/flags/cn.png',
|
||||
'ci' => __DIR__.'/flags/ci.png',
|
||||
'cm' => __DIR__.'/flags/cm.png',
|
||||
'cd' => __DIR__.'/flags/cd.png',
|
||||
'cg' => __DIR__.'/flags/cg.png',
|
||||
'co' => __DIR__.'/flags/co.png',
|
||||
'km' => __DIR__.'/flags/km.png',
|
||||
'cv' => __DIR__.'/flags/cv.png',
|
||||
'cr' => __DIR__.'/flags/cr.png',
|
||||
'cu' => __DIR__.'/flags/cu.png',
|
||||
'cy' => __DIR__.'/flags/cy.png',
|
||||
'cz' => __DIR__.'/flags/cz.png',
|
||||
'de' => __DIR__.'/flags/de.png',
|
||||
'dj' => __DIR__.'/flags/dj.png',
|
||||
'dm' => __DIR__.'/flags/dm.png',
|
||||
'dk' => __DIR__.'/flags/dk.png',
|
||||
'do' => __DIR__.'/flags/do.png',
|
||||
'dz' => __DIR__.'/flags/dz.png',
|
||||
'ec' => __DIR__.'/flags/ec.png',
|
||||
'eg' => __DIR__.'/flags/eg.png',
|
||||
'er' => __DIR__.'/flags/er.png',
|
||||
'es' => __DIR__.'/flags/es.png',
|
||||
'ee' => __DIR__.'/flags/ee.png',
|
||||
'et' => __DIR__.'/flags/et.png',
|
||||
'fi' => __DIR__.'/flags/fi.png',
|
||||
'fj' => __DIR__.'/flags/fj.png',
|
||||
'fr' => __DIR__.'/flags/fr.png',
|
||||
'fm' => __DIR__.'/flags/fm.png',
|
||||
'ga' => __DIR__.'/flags/ga.png',
|
||||
'gb' => __DIR__.'/flags/gb.png',
|
||||
'ge' => __DIR__.'/flags/ge.png',
|
||||
'gh' => __DIR__.'/flags/gh.png',
|
||||
'gn' => __DIR__.'/flags/gn.png',
|
||||
'gm' => __DIR__.'/flags/gm.png',
|
||||
'gw' => __DIR__.'/flags/gw.png',
|
||||
'gq' => __DIR__.'/flags/gq.png',
|
||||
'gr' => __DIR__.'/flags/gr.png',
|
||||
'gd' => __DIR__.'/flags/gd.png',
|
||||
'gt' => __DIR__.'/flags/gt.png',
|
||||
'gy' => __DIR__.'/flags/gy.png',
|
||||
'hn' => __DIR__.'/flags/hn.png',
|
||||
'hr' => __DIR__.'/flags/hr.png',
|
||||
'ht' => __DIR__.'/flags/ht.png',
|
||||
'hu' => __DIR__.'/flags/hu.png',
|
||||
'id' => __DIR__.'/flags/id.png',
|
||||
'in' => __DIR__.'/flags/in.png',
|
||||
'ie' => __DIR__.'/flags/ie.png',
|
||||
'ir' => __DIR__.'/flags/ir.png',
|
||||
'iq' => __DIR__.'/flags/iq.png',
|
||||
'is' => __DIR__.'/flags/is.png',
|
||||
'il' => __DIR__.'/flags/il.png',
|
||||
'it' => __DIR__.'/flags/it.png',
|
||||
'jm' => __DIR__.'/flags/jm.png',
|
||||
'jo' => __DIR__.'/flags/jo.png',
|
||||
'jp' => __DIR__.'/flags/jp.png',
|
||||
'kz' => __DIR__.'/flags/kz.png',
|
||||
'ke' => __DIR__.'/flags/ke.png',
|
||||
'kg' => __DIR__.'/flags/kg.png',
|
||||
'kh' => __DIR__.'/flags/kh.png',
|
||||
'ki' => __DIR__.'/flags/ki.png',
|
||||
'kn' => __DIR__.'/flags/kn.png',
|
||||
'kr' => __DIR__.'/flags/kr.png',
|
||||
'kw' => __DIR__.'/flags/kw.png',
|
||||
'la' => __DIR__.'/flags/la.png',
|
||||
'lb' => __DIR__.'/flags/lb.png',
|
||||
'lr' => __DIR__.'/flags/lr.png',
|
||||
'ly' => __DIR__.'/flags/ly.png',
|
||||
'lc' => __DIR__.'/flags/lc.png',
|
||||
'li' => __DIR__.'/flags/li.png',
|
||||
'lk' => __DIR__.'/flags/lk.png',
|
||||
'ls' => __DIR__.'/flags/ls.png',
|
||||
'lt' => __DIR__.'/flags/ls.png',
|
||||
'lu' => __DIR__.'/flags/lu.png',
|
||||
'lv' => __DIR__.'/flags/lv.png',
|
||||
'ma' => __DIR__.'/flags/ma.png',
|
||||
'mc' => __DIR__.'/flags/mc.png',
|
||||
'md' => __DIR__.'/flags/md.png',
|
||||
'mg' => __DIR__.'/flags/mg.png',
|
||||
'mv' => __DIR__.'/flags/mv.png',
|
||||
'mx' => __DIR__.'/flags/mx.png',
|
||||
'mh' => __DIR__.'/flags/mh.png',
|
||||
'mk' => __DIR__.'/flags/mk.png',
|
||||
'ml' => __DIR__.'/flags/ml.png',
|
||||
'mt' => __DIR__.'/flags/mt.png',
|
||||
'mm' => __DIR__.'/flags/mm.png',
|
||||
'me' => __DIR__.'/flags/me.png',
|
||||
'mn' => __DIR__.'/flags/mn.png',
|
||||
'mz' => __DIR__.'/flags/mz.png',
|
||||
'mr' => __DIR__.'/flags/mr.png',
|
||||
'mu' => __DIR__.'/flags/mu.png',
|
||||
'mw' => __DIR__.'/flags/mw.png',
|
||||
'my' => __DIR__.'/flags/my.png',
|
||||
'na' => __DIR__.'/flags/na.png',
|
||||
'ne' => __DIR__.'/flags/ne.png',
|
||||
'ng' => __DIR__.'/flags/ng.png',
|
||||
'ni' => __DIR__.'/flags/ni.png',
|
||||
'nl' => __DIR__.'/flags/nl.png',
|
||||
'no' => __DIR__.'/flags/no.png',
|
||||
'np' => __DIR__.'/flags/np.png',
|
||||
'nr' => __DIR__.'/flags/nr.png',
|
||||
'nz' => __DIR__.'/flags/nz.png',
|
||||
'om' => __DIR__.'/flags/om.png',
|
||||
'pk' => __DIR__.'/flags/pk.png',
|
||||
'pa' => __DIR__.'/flags/pa.png',
|
||||
'pe' => __DIR__.'/flags/pe.png',
|
||||
'ph' => __DIR__.'/flags/ph.png',
|
||||
'pw' => __DIR__.'/flags/pw.png',
|
||||
'pg' => __DIR__.'/flags/pg.png',
|
||||
'pl' => __DIR__.'/flags/pl.png',
|
||||
'kp' => __DIR__.'/flags/kp.png',
|
||||
'pt' => __DIR__.'/flags/pt.png',
|
||||
'py' => __DIR__.'/flags/py.png',
|
||||
'qa' => __DIR__.'/flags/qa.png',
|
||||
'ro' => __DIR__.'/flags/ro.png',
|
||||
'ru' => __DIR__.'/flags/ru.png',
|
||||
'rw' => __DIR__.'/flags/rw.png',
|
||||
'sa' => __DIR__.'/flags/sa.png',
|
||||
'sd' => __DIR__.'/flags/sd.png',
|
||||
'sn' => __DIR__.'/flags/sn.png',
|
||||
'sg' => __DIR__.'/flags/sg.png',
|
||||
'sb' => __DIR__.'/flags/sb.png',
|
||||
'sl' => __DIR__.'/flags/sl.png',
|
||||
'sv' => __DIR__.'/flags/sv.png',
|
||||
'sm' => __DIR__.'/flags/sm.png',
|
||||
'so' => __DIR__.'/flags/so.png',
|
||||
'rs' => __DIR__.'/flags/rs.png',
|
||||
'ss' => __DIR__.'/flags/ss.png',
|
||||
'st' => __DIR__.'/flags/st.png',
|
||||
'sr' => __DIR__.'/flags/sr.png',
|
||||
'sk' => __DIR__.'/flags/sk.png',
|
||||
'si' => __DIR__.'/flags/si.png',
|
||||
'se' => __DIR__.'/flags/se.png',
|
||||
'sz' => __DIR__.'/flags/sz.png',
|
||||
'sc' => __DIR__.'/flags/sc.png',
|
||||
'sy' => __DIR__.'/flags/sy.png',
|
||||
'td' => __DIR__.'/flags/td.png',
|
||||
'tg' => __DIR__.'/flags/tg.png',
|
||||
'th' => __DIR__.'/flags/th.png',
|
||||
'tj' => __DIR__.'/flags/tj.png',
|
||||
'tm' => __DIR__.'/flags/tm.png',
|
||||
'tl' => __DIR__.'/flags/tl.png',
|
||||
'to' => __DIR__.'/flags/to.png',
|
||||
'tt' => __DIR__.'/flags/tt.png',
|
||||
'tn' => __DIR__.'/flags/tn.png',
|
||||
'tr' => __DIR__.'/flags/tr.png',
|
||||
'tv' => __DIR__.'/flags/tv.png',
|
||||
'tz' => __DIR__.'/flags/tz.png',
|
||||
'ug' => __DIR__.'/flags/ug.png',
|
||||
'ua' => __DIR__.'/flags/ua.png',
|
||||
'uy' => __DIR__.'/flags/uy.png',
|
||||
'us' => __DIR__.'/flags/us.png',
|
||||
'uz' => __DIR__.'/flags/uz.png',
|
||||
'va' => __DIR__.'/flags/va.png',
|
||||
'vc' => __DIR__.'/flags/vc.png',
|
||||
've' => __DIR__.'/flags/ve.png',
|
||||
'vn' => __DIR__.'/flags/vn.png',
|
||||
'vu' => __DIR__.'/flags/vu.png',
|
||||
'ws' => __DIR__.'/flags/ws.png',
|
||||
'ye' => __DIR__.'/flags/ye.png',
|
||||
'za' => __DIR__.'/flags/za.png',
|
||||
'zm' => __DIR__.'/flags/zm.png',
|
||||
'zw' => __DIR__.'/flags/zw.png',
|
||||
'af' => __DIR__ . '/flags/af.png',
|
||||
'ao' => __DIR__ . '/flags/ao.png',
|
||||
'al' => __DIR__ . '/flags/al.png',
|
||||
'ad' => __DIR__ . '/flags/ad.png',
|
||||
'ae' => __DIR__ . '/flags/ae.png',
|
||||
'ar' => __DIR__ . '/flags/ar.png',
|
||||
'am' => __DIR__ . '/flags/am.png',
|
||||
'ag' => __DIR__ . '/flags/ag.png',
|
||||
'au' => __DIR__ . '/flags/au.png',
|
||||
'at' => __DIR__ . '/flags/at.png',
|
||||
'az' => __DIR__ . '/flags/az.png',
|
||||
'bi' => __DIR__ . '/flags/bi.png',
|
||||
'be' => __DIR__ . '/flags/be.png',
|
||||
'bj' => __DIR__ . '/flags/bj.png',
|
||||
'bf' => __DIR__ . '/flags/bf.png',
|
||||
'bd' => __DIR__ . '/flags/bd.png',
|
||||
'bg' => __DIR__ . '/flags/bg.png',
|
||||
'bh' => __DIR__ . '/flags/bh.png',
|
||||
'bs' => __DIR__ . '/flags/bs.png',
|
||||
'ba' => __DIR__ . '/flags/ba.png',
|
||||
'by' => __DIR__ . '/flags/by.png',
|
||||
'bz' => __DIR__ . '/flags/bz.png',
|
||||
'bo' => __DIR__ . '/flags/bo.png',
|
||||
'br' => __DIR__ . '/flags/br.png',
|
||||
'bb' => __DIR__ . '/flags/bb.png',
|
||||
'bn' => __DIR__ . '/flags/bn.png',
|
||||
'bt' => __DIR__ . '/flags/bt.png',
|
||||
'bw' => __DIR__ . '/flags/bw.png',
|
||||
'cf' => __DIR__ . '/flags/cf.png',
|
||||
'ca' => __DIR__ . '/flags/ca.png',
|
||||
'ch' => __DIR__ . '/flags/ch.png',
|
||||
'cl' => __DIR__ . '/flags/cl.png',
|
||||
'cn' => __DIR__ . '/flags/cn.png',
|
||||
'ci' => __DIR__ . '/flags/ci.png',
|
||||
'cm' => __DIR__ . '/flags/cm.png',
|
||||
'cd' => __DIR__ . '/flags/cd.png',
|
||||
'cg' => __DIR__ . '/flags/cg.png',
|
||||
'co' => __DIR__ . '/flags/co.png',
|
||||
'km' => __DIR__ . '/flags/km.png',
|
||||
'cv' => __DIR__ . '/flags/cv.png',
|
||||
'cr' => __DIR__ . '/flags/cr.png',
|
||||
'cu' => __DIR__ . '/flags/cu.png',
|
||||
'cy' => __DIR__ . '/flags/cy.png',
|
||||
'cz' => __DIR__ . '/flags/cz.png',
|
||||
'de' => __DIR__ . '/flags/de.png',
|
||||
'dj' => __DIR__ . '/flags/dj.png',
|
||||
'dm' => __DIR__ . '/flags/dm.png',
|
||||
'dk' => __DIR__ . '/flags/dk.png',
|
||||
'do' => __DIR__ . '/flags/do.png',
|
||||
'dz' => __DIR__ . '/flags/dz.png',
|
||||
'ec' => __DIR__ . '/flags/ec.png',
|
||||
'eg' => __DIR__ . '/flags/eg.png',
|
||||
'er' => __DIR__ . '/flags/er.png',
|
||||
'es' => __DIR__ . '/flags/es.png',
|
||||
'ee' => __DIR__ . '/flags/ee.png',
|
||||
'et' => __DIR__ . '/flags/et.png',
|
||||
'fi' => __DIR__ . '/flags/fi.png',
|
||||
'fj' => __DIR__ . '/flags/fj.png',
|
||||
'fr' => __DIR__ . '/flags/fr.png',
|
||||
'fm' => __DIR__ . '/flags/fm.png',
|
||||
'ga' => __DIR__ . '/flags/ga.png',
|
||||
'gb' => __DIR__ . '/flags/gb.png',
|
||||
'ge' => __DIR__ . '/flags/ge.png',
|
||||
'gh' => __DIR__ . '/flags/gh.png',
|
||||
'gn' => __DIR__ . '/flags/gn.png',
|
||||
'gm' => __DIR__ . '/flags/gm.png',
|
||||
'gw' => __DIR__ . '/flags/gw.png',
|
||||
'gq' => __DIR__ . '/flags/gq.png',
|
||||
'gr' => __DIR__ . '/flags/gr.png',
|
||||
'gd' => __DIR__ . '/flags/gd.png',
|
||||
'gt' => __DIR__ . '/flags/gt.png',
|
||||
'gy' => __DIR__ . '/flags/gy.png',
|
||||
'hn' => __DIR__ . '/flags/hn.png',
|
||||
'hr' => __DIR__ . '/flags/hr.png',
|
||||
'ht' => __DIR__ . '/flags/ht.png',
|
||||
'hu' => __DIR__ . '/flags/hu.png',
|
||||
'id' => __DIR__ . '/flags/id.png',
|
||||
'in' => __DIR__ . '/flags/in.png',
|
||||
'ie' => __DIR__ . '/flags/ie.png',
|
||||
'ir' => __DIR__ . '/flags/ir.png',
|
||||
'iq' => __DIR__ . '/flags/iq.png',
|
||||
'is' => __DIR__ . '/flags/is.png',
|
||||
'il' => __DIR__ . '/flags/il.png',
|
||||
'it' => __DIR__ . '/flags/it.png',
|
||||
'jm' => __DIR__ . '/flags/jm.png',
|
||||
'jo' => __DIR__ . '/flags/jo.png',
|
||||
'jp' => __DIR__ . '/flags/jp.png',
|
||||
'kz' => __DIR__ . '/flags/kz.png',
|
||||
'ke' => __DIR__ . '/flags/ke.png',
|
||||
'kg' => __DIR__ . '/flags/kg.png',
|
||||
'kh' => __DIR__ . '/flags/kh.png',
|
||||
'ki' => __DIR__ . '/flags/ki.png',
|
||||
'kn' => __DIR__ . '/flags/kn.png',
|
||||
'kr' => __DIR__ . '/flags/kr.png',
|
||||
'kw' => __DIR__ . '/flags/kw.png',
|
||||
'la' => __DIR__ . '/flags/la.png',
|
||||
'lb' => __DIR__ . '/flags/lb.png',
|
||||
'lr' => __DIR__ . '/flags/lr.png',
|
||||
'ly' => __DIR__ . '/flags/ly.png',
|
||||
'lc' => __DIR__ . '/flags/lc.png',
|
||||
'li' => __DIR__ . '/flags/li.png',
|
||||
'lk' => __DIR__ . '/flags/lk.png',
|
||||
'ls' => __DIR__ . '/flags/ls.png',
|
||||
'lt' => __DIR__ . '/flags/ls.png',
|
||||
'lu' => __DIR__ . '/flags/lu.png',
|
||||
'lv' => __DIR__ . '/flags/lv.png',
|
||||
'ma' => __DIR__ . '/flags/ma.png',
|
||||
'mc' => __DIR__ . '/flags/mc.png',
|
||||
'md' => __DIR__ . '/flags/md.png',
|
||||
'mg' => __DIR__ . '/flags/mg.png',
|
||||
'mv' => __DIR__ . '/flags/mv.png',
|
||||
'mx' => __DIR__ . '/flags/mx.png',
|
||||
'mh' => __DIR__ . '/flags/mh.png',
|
||||
'mk' => __DIR__ . '/flags/mk.png',
|
||||
'ml' => __DIR__ . '/flags/ml.png',
|
||||
'mt' => __DIR__ . '/flags/mt.png',
|
||||
'mm' => __DIR__ . '/flags/mm.png',
|
||||
'me' => __DIR__ . '/flags/me.png',
|
||||
'mn' => __DIR__ . '/flags/mn.png',
|
||||
'mz' => __DIR__ . '/flags/mz.png',
|
||||
'mr' => __DIR__ . '/flags/mr.png',
|
||||
'mu' => __DIR__ . '/flags/mu.png',
|
||||
'mw' => __DIR__ . '/flags/mw.png',
|
||||
'my' => __DIR__ . '/flags/my.png',
|
||||
'na' => __DIR__ . '/flags/na.png',
|
||||
'ne' => __DIR__ . '/flags/ne.png',
|
||||
'ng' => __DIR__ . '/flags/ng.png',
|
||||
'ni' => __DIR__ . '/flags/ni.png',
|
||||
'nl' => __DIR__ . '/flags/nl.png',
|
||||
'no' => __DIR__ . '/flags/no.png',
|
||||
'np' => __DIR__ . '/flags/np.png',
|
||||
'nr' => __DIR__ . '/flags/nr.png',
|
||||
'nz' => __DIR__ . '/flags/nz.png',
|
||||
'om' => __DIR__ . '/flags/om.png',
|
||||
'pk' => __DIR__ . '/flags/pk.png',
|
||||
'pa' => __DIR__ . '/flags/pa.png',
|
||||
'pe' => __DIR__ . '/flags/pe.png',
|
||||
'ph' => __DIR__ . '/flags/ph.png',
|
||||
'pw' => __DIR__ . '/flags/pw.png',
|
||||
'pg' => __DIR__ . '/flags/pg.png',
|
||||
'pl' => __DIR__ . '/flags/pl.png',
|
||||
'kp' => __DIR__ . '/flags/kp.png',
|
||||
'pt' => __DIR__ . '/flags/pt.png',
|
||||
'py' => __DIR__ . '/flags/py.png',
|
||||
'qa' => __DIR__ . '/flags/qa.png',
|
||||
'ro' => __DIR__ . '/flags/ro.png',
|
||||
'ru' => __DIR__ . '/flags/ru.png',
|
||||
'rw' => __DIR__ . '/flags/rw.png',
|
||||
'sa' => __DIR__ . '/flags/sa.png',
|
||||
'sd' => __DIR__ . '/flags/sd.png',
|
||||
'sn' => __DIR__ . '/flags/sn.png',
|
||||
'sg' => __DIR__ . '/flags/sg.png',
|
||||
'sb' => __DIR__ . '/flags/sb.png',
|
||||
'sl' => __DIR__ . '/flags/sl.png',
|
||||
'sv' => __DIR__ . '/flags/sv.png',
|
||||
'sm' => __DIR__ . '/flags/sm.png',
|
||||
'so' => __DIR__ . '/flags/so.png',
|
||||
'rs' => __DIR__ . '/flags/rs.png',
|
||||
'ss' => __DIR__ . '/flags/ss.png',
|
||||
'st' => __DIR__ . '/flags/st.png',
|
||||
'sr' => __DIR__ . '/flags/sr.png',
|
||||
'sk' => __DIR__ . '/flags/sk.png',
|
||||
'si' => __DIR__ . '/flags/si.png',
|
||||
'se' => __DIR__ . '/flags/se.png',
|
||||
'sz' => __DIR__ . '/flags/sz.png',
|
||||
'sc' => __DIR__ . '/flags/sc.png',
|
||||
'sy' => __DIR__ . '/flags/sy.png',
|
||||
'td' => __DIR__ . '/flags/td.png',
|
||||
'tg' => __DIR__ . '/flags/tg.png',
|
||||
'th' => __DIR__ . '/flags/th.png',
|
||||
'tj' => __DIR__ . '/flags/tj.png',
|
||||
'tm' => __DIR__ . '/flags/tm.png',
|
||||
'tl' => __DIR__ . '/flags/tl.png',
|
||||
'to' => __DIR__ . '/flags/to.png',
|
||||
'tt' => __DIR__ . '/flags/tt.png',
|
||||
'tn' => __DIR__ . '/flags/tn.png',
|
||||
'tr' => __DIR__ . '/flags/tr.png',
|
||||
'tv' => __DIR__ . '/flags/tv.png',
|
||||
'tz' => __DIR__ . '/flags/tz.png',
|
||||
'ug' => __DIR__ . '/flags/ug.png',
|
||||
'ua' => __DIR__ . '/flags/ua.png',
|
||||
'uy' => __DIR__ . '/flags/uy.png',
|
||||
'us' => __DIR__ . '/flags/us.png',
|
||||
'uz' => __DIR__ . '/flags/uz.png',
|
||||
'va' => __DIR__ . '/flags/va.png',
|
||||
'vc' => __DIR__ . '/flags/vc.png',
|
||||
've' => __DIR__ . '/flags/ve.png',
|
||||
'vn' => __DIR__ . '/flags/vn.png',
|
||||
'vu' => __DIR__ . '/flags/vu.png',
|
||||
'ws' => __DIR__ . '/flags/ws.png',
|
||||
'ye' => __DIR__ . '/flags/ye.png',
|
||||
'za' => __DIR__ . '/flags/za.png',
|
||||
'zm' => __DIR__ . '/flags/zm.png',
|
||||
'zw' => __DIR__ . '/flags/zw.png',
|
||||
];
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
return [
|
||||
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
|
||||
'AND' => __DIR__.'/os/android.png',
|
||||
'ATV' => __DIR__.'/os/apple-tv.png',
|
||||
'COS' => __DIR__.'/os/chrome-os.png',
|
||||
'AND' => __DIR__ . '/os/android.png',
|
||||
'ATV' => __DIR__ . '/os/apple-tv.png',
|
||||
'COS' => __DIR__ . '/os/chrome-os.png',
|
||||
|
||||
/*
|
||||
'AIX' => 'AIX',
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[1297371,1759475,6360216,20852629,5857008,19310830,9708641,26739219,23742426,22174310,77877486,1477010,29069505,62933155,45863583,42211,176163,58045728,7091609,31401437,1911066,66096031,22432834,43054051,79051850,22895284,100597998,91385411,49699333,7818620,11004008,54898623,27856297,33250853,49375670,30630364,50206,47822499,7481165,52557347,13681567,20492520,51369094,51821861,50256986,28431370,13692220,24373771,15938422,27148250,14805534,36137226,36071208,40193621,56179878,53281158,48085134,19358691,32528768,19733683,37348419,13719696,18309412,42106787,53618500,5306011,4408379,35486736,18586611,15062564,27011453,56090587,44623032,25107942,81188,33922418,16717633,32809211,69401139,25815659,4104127,20889958,3102249,12476526,2635185,28495651,50957556,2791280,31023616,20955511,835733,471907,69008866,44906587,45271396,76054330,45748739,41908747,23402178,47356149,8921,36632821,3668741,71702982,29725587,26303198,785830,51410502,60089135,2847349,43172716,48546075,8216525,41161981,51828039,4334997,80918302,38534289,47860497,80036766,41341387,49818988,58487637,29237374,46913894,5148229,4377199,29686102,26272249,75117692,4090256,27357868,33062368,38664231,46695441,743291,22633385,6368283,11593067,45097959,43381712,3284228,1972717,33012425,61755381,25405707,3144291,44156359,5497267,7423905,20716175,28586681,5975506,23518097,22187384,24191952,7768078,971530,51240166,55633427,34207400,77061285,11719476,35950229,66742927,34406802,802933,50047839,39148877,26602940,9693472,44273767,19362725,31209978,30521594,686298,6237394,35039730,42580581,36671793,8502129,8466918,81866614,54903252,28373606,13381361,72331432,30694270,5355510,8209163,86675510,9453522,42496309,56145786,2149381,393945,22084723,52621436,8872447,5575392,29619660,5547479,8852116,11151445,4717349,17725274,65615065,18537755,29292618,53044263,26597930,10313411,55998629,77529288,17404636,33729848,19422168,17916404,66111735,10329006,33502846,398230,81643826,105039167,47522632,91655303,9774614,10603631,284924,60857954,22885912,116552306,36103454,794606,27729549,1754457,36594527,13899668,78664749,47406531,27698189,5305654,53345517,6756412,29176704,77790497,47504894,37251540,52361778,52200375,1351177,66022861,73975409,25745396,31433638,37118134,43210805,20317665,11923975,47187468,16362381,36751163,14959876,32362757,65529384,52352285,74085816,3628535,43902034,75667593,26132902,466713,617558,96806061,33605526,11290524,43621940,12446314,17146935,55018955,56096559,79797000,40014186,34449936,58387964,23368207,42414965,44056349,33743031,12294525,58251592,33755729,9021747,932084,11428067,97121933,80122730,60894542,58583793,56051809,32243289,9934371,90936802,74638775,65399526,77604,64524822,47782249,43633955,42793632,55969597,72334601,82395440,92818577,60866204,65016769,23725091,45892107,55308895,86314140,82756460,47685349,63562160,73419211,1613216,50882624,91469717,46166258,60927324,41763158,83607556,2171717,50497814,39427312,61322830,40076195,39419448,29397545,55090719,53259730,20885012,64558515,69677883,55741087,72426535,46033036,68477507,30376878,73700530,25518600,29922887,36229969,47573417,40424087,49054503,16880385,22801227,72848513,64347914,814402,49149679,55017867,49481876,67067955,31439735,63878173,80322286,43746210,17332970,22702905,62476876,89888292,75736952,54059881,90782137,63588969,57111920,63330165,70258211,46371923,17837758,59364507,52203828,60147326,18481195,74822422,9803078,67309607,60410049,47360939,19922556,90848252,24698014,58886915,63579762,96648934,68523530,60518745,37345795,3929651,54993657,52061363,43019989,5787917,94674993,71593494,17143469,10288548,1830380,71510505,59124772,2335145,70798495,46474346,49263351,52062536,63151043,65248303,26071571,53626355,43992469,60785452,63467479,71837281,19490891,58628586,38250310,7271718,1110414,57227290,11625672,85063520,88965873,70096901,42029519,85363195,64471630,69353350,66922161,2221746,100430077,12299813,62690310,68282006,99184676,2450,22989561,22212661,59973863,11232940,76688923,22321353,77732479,84286404,32268377,34828782,23068019,57074509,24620969,20735983,26173690,75809937,49760818,86646105,52617262]
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"eldad@appwrite.io": { "memberSince": "2020-10-15", "spot": "0", "gitHub": "eldadfux" },
|
||||
"christy@appwrite.io": { "memberSince": "2020-12-01", "spot": "1", "gitHub": "christyjacob4" },
|
||||
"torsten@appwrite.io": { "memberSince": "2020-12-28", "spot": "2", "gitHub": "torstendittmann" },
|
||||
"damodar@appwrite.io": { "memberSince": "2021-01-02", "spot": "3", "gitHub": "lohanidamodar" },
|
||||
"bradley@appwrite.io": { "memberSince": "2021-05-21", "spot": "5", "gitHub": "PineappleIOnic" },
|
||||
"jake@appwrite.io": { "memberSince": "2021-06-28", "spot": "6", "gitHub": "abnegate" },
|
||||
"sara@appwrite.io": { "memberSince": "2021-08-16", "spot": "7", "gitHub": "sarakaandorp" },
|
||||
"matej@appwrite.io": { "memberSince": "2021-08-23", "spot": "8", "gitHub": "meldiron" },
|
||||
"aditya@appwrite.io": { "memberSince": "2021-09-01", "spot": "9", "gitHub": "adityaoberai" },
|
||||
"wess@appwrite.io": { "memberSince": "2021-11-08", "spot": "12", "gitHub": "wess" },
|
||||
"may@appwrite.io": { "memberSince": "2021-11-28", "spot": "14", "gitHub": "MayEnder" },
|
||||
"elad@appwrite.io": { "memberSince": "2021-12-19", "spot": "15", "gitHub": "elad2412" },
|
||||
"vincent@appwrite.io": { "memberSince": "2022-01-01", "spot": "16", "gitHub": "gewenyu99" },
|
||||
"haimantika@appwrite.io": { "memberSince": "2022-04-01", "spot": "18", "gitHub": "Haimantika" },
|
||||
"chen@appwrite.io": { "memberSince": "2022-01-24", "spot": "19", "gitHub": "chenparnasa" },
|
||||
"tessa@appwrite.io": { "memberSince": "2022-04-21", "spot": "20", "gitHub": "tessamero" },
|
||||
"shimon@appwrite.io": { "memberSince": "2022-05-01", "spot": "23", "gitHub": "shimonewman" },
|
||||
"shmuel@appwrite.io": { "memberSince": "2022-03-20", "spot": "24", "gitHub": "fogelito" },
|
||||
"arman@appwrite.io": { "memberSince": "2022-04-04", "spot": "25", "gitHub": "ArmanNik" },
|
||||
"carla@appwrite.io": { "memberSince": "2022-04-04", "spot": "26", "gitHub": "heyCarla" },
|
||||
"emma@appwrite.io": { "memberSince": "2022-05-08", "spot": "27", "gitHub": "emmacarpagnano1" },
|
||||
"dylan@appwrite.io": { "memberSince": "2022-05-09", "spot": "28", "gitHub": "DylanG-64" },
|
||||
"steven@appwrite.io": { "memberSince": "2022-07-01", "spot": "30", "gitHub": "stnguyen90" },
|
||||
"jyoti@appwrite.io": { "memberSince": "2022-10-24", "spot": "31", "gitHub": "joeyouss" },
|
||||
"jade@appwrite.io": { "memberSince": "2022-10-31", "spot": "32", "gitHub": "dajebp" },
|
||||
"khushboo@appwrite.io": { "memberSince": "2021-11-08", "spot": "13", "gitHub": "vermakhushboo" },
|
||||
"thomas@appwrite.io": { "memberSince": "2022-11-03", "spot": "34", "gitHub": "TGlide" },
|
||||
"holly@appwrite.io": { "memberSince": "2022-12-05", "spot": "35", "gitHub": "HollyBarclay" },
|
||||
"laura@appwrite.io": { "memberSince": "2023-01-25", "spot": "36", "gitHub": "LauraDuRy" },
|
||||
"caio@appwrite.io": { "memberSince": "2023-03-27", "spot": "37", "gitHub": "ariascaio" },
|
||||
"luke@appwrite.io": { "memberSince": "2023-05-04", "spot": "38", "gitHub": "loks0n" }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"bishwajeet.techmaster@gmail.com": { "memberSince": "2023-02-07" },
|
||||
"lucasaudart@gmail.com": { "memberSince": "2023-02-07" },
|
||||
"tkarmakar27112000@gmail.com": { "memberSince": "2023-02-07" },
|
||||
"alves.mckl@gmail.com": { "memberSince": "2023-02-07" },
|
||||
"dpns_nampula@rnlay.com": { "memberSince": "2023-02-07" },
|
||||
"a.stephensimon@outlook.com": { "memberSince": "2023-02-07" },
|
||||
"hidianapham@gmail.com": { "memberSince": "2023-02-07" }
|
||||
}
|
||||
+2686
-1877
File diff suppressed because it is too large
Load Diff
+96
-24
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* List of server wide error codes and their respective messages.
|
||||
* List of server wide error codes and their respective messages.
|
||||
*/
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
@@ -11,17 +11,17 @@ return [
|
||||
Exception::GENERAL_UNKNOWN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN,
|
||||
'description' => 'An unknown error has occured. Please check the logs for more information.',
|
||||
'code' => 500,
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::GENERAL_MOCK => [
|
||||
'name' => Exception::GENERAL_MOCK,
|
||||
'description' => 'General errors thrown by the mock controller used for testing.',
|
||||
'code' => 400,
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_ACCESS_FORBIDDEN => [
|
||||
'name' => Exception::GENERAL_ACCESS_FORBIDDEN,
|
||||
'description' => 'Access to this API is forbidden.',
|
||||
'code' => 401,
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_UNKNOWN_ORIGIN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
|
||||
@@ -48,6 +48,11 @@ return [
|
||||
'description' => 'SMTP is disabled on your Appwrite instance. You can <a href="/docs/email-delivery">learn more about setting up SMTP</a> in our docs.',
|
||||
'code' => 503,
|
||||
],
|
||||
Exception::GENERAL_PHONE_DISABLED => [
|
||||
'name' => Exception::GENERAL_PHONE_DISABLED,
|
||||
'description' => 'Phone provider is not configured. Please check the _APP_SMS_PROVIDER environment variable of your Appwrite server.',
|
||||
'code' => 503,
|
||||
],
|
||||
Exception::GENERAL_ARGUMENT_INVALID => [
|
||||
'name' => Exception::GENERAL_ARGUMENT_INVALID,
|
||||
'description' => 'The request contains one or more invalid arguments. Please refer to the endpoint documentation.',
|
||||
@@ -65,7 +70,7 @@ return [
|
||||
],
|
||||
Exception::GENERAL_ROUTE_NOT_FOUND => [
|
||||
'name' => Exception::GENERAL_ROUTE_NOT_FOUND,
|
||||
'description' => 'The requested route was not found. Please refer to the docs and try again.',
|
||||
'description' => 'The requested route was not found. Please refer to the API docs and try again.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::GENERAL_CURSOR_NOT_FOUND => [
|
||||
@@ -81,7 +86,12 @@ return [
|
||||
Exception::GENERAL_PROTOCOL_UNSUPPORTED => [
|
||||
'name' => Exception::GENERAL_PROTOCOL_UNSUPPORTED,
|
||||
'description' => 'The request cannot be fulfilled with the current protocol. Please check the value of the _APP_OPTIONS_FORCE_HTTPS environment variable.',
|
||||
'code' => 500,
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::GENERAL_CODES_DISABLED => [
|
||||
'name' => Exception::GENERAL_CODES_DISABLED,
|
||||
'description' => 'Invitation codes are disabled on this server. Please contact the server administrator.',
|
||||
'code' => 500,
|
||||
],
|
||||
|
||||
/** User Errors */
|
||||
@@ -97,12 +107,12 @@ return [
|
||||
],
|
||||
Exception::USER_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_ALREADY_EXISTS,
|
||||
'description' => 'A user with the same email ID already exists in your project.',
|
||||
'description' => 'A user with the same email already exists in your project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_BLOCKED => [
|
||||
'name' => Exception::USER_BLOCKED,
|
||||
'description' => 'The current user has been blocked. You can unblock the user from the Appwrite console.',
|
||||
'description' => 'The current user has been blocked.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_INVALID_TOKEN => [
|
||||
@@ -117,12 +127,17 @@ return [
|
||||
],
|
||||
Exception::USER_EMAIL_NOT_WHITELISTED => [
|
||||
'name' => Exception::USER_EMAIL_NOT_WHITELISTED,
|
||||
'description' => 'The user\'s email is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_EMAILS environment variable of your Appwrite server.',
|
||||
'description' => 'Console registration is restricted to specific emails. Contact your administrator for more information.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_INVALID_CODE => [
|
||||
'name' => Exception::USER_INVALID_CODE,
|
||||
'description' => 'The specified code is not valid. Contact your administrator for more information.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_IP_NOT_WHITELISTED => [
|
||||
'name' => Exception::USER_IP_NOT_WHITELISTED,
|
||||
'description' => 'The user\'s IP address is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_IPS environment variable of your Appwrite server.',
|
||||
'description' => 'Console registration is restricted to specific IPs. Contact your administrator for more information.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_INVALID_CREDENTIALS => [
|
||||
@@ -147,7 +162,7 @@ return [
|
||||
],
|
||||
Exception::USER_EMAIL_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_EMAIL_ALREADY_EXISTS,
|
||||
'description' => 'Another user with the same email already exists in the current project.',
|
||||
'description' => 'A user with the same email already exists in the current project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_PASSWORD_MISMATCH => [
|
||||
@@ -170,6 +185,21 @@ return [
|
||||
'description' => 'The requested authentication method is either disabled or unsupported. Please check the supported authentication methods in the Appwrite console.',
|
||||
'code' => 501,
|
||||
],
|
||||
Exception::USER_PHONE_ALREADY_EXISTS => [
|
||||
'name' => Exception::USER_PHONE_ALREADY_EXISTS,
|
||||
'description' => 'A user with the same phone number already exists in the current project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::USER_PHONE_NOT_FOUND => [
|
||||
'name' => Exception::USER_PHONE_NOT_FOUND,
|
||||
'description' => 'The current user does not have a phone number associated with their account.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::USER_MISSING_ID => [
|
||||
'name' => Exception::USER_MISSING_ID,
|
||||
'description' => 'Missing ID from OAuth2 provider.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Teams */
|
||||
Exception::TEAM_NOT_FOUND => [
|
||||
@@ -179,7 +209,7 @@ return [
|
||||
],
|
||||
Exception::TEAM_INVITE_ALREADY_EXISTS => [
|
||||
'name' => Exception::TEAM_INVITE_ALREADY_EXISTS,
|
||||
'description' => 'The current user has already received an invitation to join the team.',
|
||||
'description' => 'User has already been invited or is already a member of this team',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::TEAM_INVITE_NOT_FOUND => [
|
||||
@@ -203,13 +233,17 @@ return [
|
||||
'code' => 401,
|
||||
],
|
||||
|
||||
|
||||
/** Membership */
|
||||
Exception::MEMBERSHIP_NOT_FOUND => [
|
||||
'name' => Exception::MEMBERSHIP_NOT_FOUND,
|
||||
'description' => 'Membership with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::MEMBERSHIP_ALREADY_CONFIRMED => [
|
||||
'name' => Exception::MEMBERSHIP_ALREADY_CONFIRMED,
|
||||
'description' => 'Membership already confirmed',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Avatars */
|
||||
Exception::AVATAR_SET_NOT_FOUND => [
|
||||
@@ -256,7 +290,7 @@ return [
|
||||
],
|
||||
Exception::STORAGE_FILE_TYPE_UNSUPPORTED => [
|
||||
'name' => Exception::STORAGE_FILE_TYPE_UNSUPPORTED,
|
||||
'description' => 'The file type is not supported.',
|
||||
'description' => 'The given file extension is not supported.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::STORAGE_INVALID_FILE_SIZE => [
|
||||
@@ -310,7 +344,7 @@ return [
|
||||
],
|
||||
Exception::BUILD_NOT_READY => [
|
||||
'name' => Exception::BUILD_NOT_READY,
|
||||
'description' => 'Build with the requested ID is builing and not ready for execution.',
|
||||
'description' => 'Build with the requested ID is building and not ready for execution.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::BUILD_IN_PROGRESS => [
|
||||
@@ -333,6 +367,19 @@ return [
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Databases */
|
||||
Exception::DATABASE_NOT_FOUND => [
|
||||
'name' => Exception::DATABASE_NOT_FOUND,
|
||||
'description' => 'Database not found',
|
||||
'code' => 404
|
||||
],
|
||||
|
||||
Exception::DATABASE_ALREADY_EXISTS => [
|
||||
'name' => Exception::DATABASE_ALREADY_EXISTS,
|
||||
'description' => 'Database already exists',
|
||||
'code' => 409
|
||||
],
|
||||
|
||||
/** Collections */
|
||||
Exception::COLLECTION_NOT_FOUND => [
|
||||
'name' => Exception::COLLECTION_NOT_FOUND,
|
||||
@@ -437,6 +484,11 @@ return [
|
||||
'description' => 'Project with the requested ID could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PROJECT_ALREADY_EXISTS => [
|
||||
'name' => Exception::PROJECT_ALREADY_EXISTS,
|
||||
'description' => 'Project with the requested ID already exists.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::PROJECT_UNKNOWN => [
|
||||
'name' => Exception::PROJECT_UNKNOWN,
|
||||
'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
|
||||
@@ -449,24 +501,29 @@ return [
|
||||
],
|
||||
Exception::PROJECT_PROVIDER_UNSUPPORTED => [
|
||||
'name' => Exception::PROJECT_PROVIDER_UNSUPPORTED,
|
||||
'description' => 'The chosen OAuth provider is unsupported. Please check <a href="/docs/client/account?sdk=web-default#accountCreateOAuth2Session"> the docs</a> for the complete list of supported OAuth providers.',
|
||||
'description' => 'The chosen OAuth provider is unsupported. Please check the <a href="/docs/client/account?sdk=web-default#accountCreateOAuth2Session">Create OAuth2 Session docs</a> for the complete list of supported OAuth providers.',
|
||||
'code' => 501,
|
||||
],
|
||||
Exception::PROJECT_INVALID_SUCCESS_URL => [
|
||||
'name' => Exception::PROJECT_INVALID_SUCCESS_URL,
|
||||
'description' => 'Invalid URL received for OAuth success redirect.',
|
||||
'description' => 'Invalid redirect URL for OAuth success.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_INVALID_FAILURE_URL => [
|
||||
'name' => Exception::PROJECT_INVALID_FAILURE_URL,
|
||||
'description' => 'Invalid URL received for OAuth failure redirect.',
|
||||
'description' => 'Invalid redirect URL for OAuth failure.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_MISSING_USER_ID => [
|
||||
'name' => Exception::PROJECT_MISSING_USER_ID,
|
||||
'description' => 'Failed to obtain user ID from the OAuth provider.',
|
||||
Exception::PROJECT_RESERVED_PROJECT => [
|
||||
'name' => Exception::PROJECT_RESERVED_PROJECT,
|
||||
'description' => 'The project ID is reserved. Please choose another project ID.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::PROJECT_KEY_EXPIRED => [
|
||||
'name' => Exception::PROJECT_KEY_EXPIRED,
|
||||
'description' => 'The project key has expired. Please generate a new key using the Appwrite console.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::WEBHOOK_NOT_FOUND => [
|
||||
'name' => Exception::WEBHOOK_NOT_FOUND,
|
||||
'description' => 'Webhook with the requested ID could not be found.',
|
||||
@@ -489,12 +546,27 @@ return [
|
||||
],
|
||||
Exception::DOMAIN_ALREADY_EXISTS => [
|
||||
'name' => Exception::DOMAIN_ALREADY_EXISTS,
|
||||
'description' => 'A Domain with the requested ID already exists.',
|
||||
'description' => 'The requested domain is currently in use by a project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::DOMAIN_FORBIDDEN => [
|
||||
'name' => Exception::DOMAIN_FORBIDDEN,
|
||||
'description' => 'The requested domain cannot be used as a custom domain.',
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::VARIABLE_NOT_FOUND => [
|
||||
'name' => Exception::VARIABLE_NOT_FOUND,
|
||||
'description' => 'Variable with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::VARIABLE_ALREADY_EXISTS => [
|
||||
'name' => Exception::VARIABLE_ALREADY_EXISTS,
|
||||
'description' => 'Variable with the same ID already exists in your project.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::DOMAIN_VERIFICATION_FAILED => [
|
||||
'name' => Exception::DOMAIN_VERIFICATION_FAILED,
|
||||
'description' => 'Domain verification for the requested domain has failed.',
|
||||
'code' => 401,
|
||||
]
|
||||
];
|
||||
],
|
||||
];
|
||||
|
||||
+49
-35
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* List of publicly accessiable system events
|
||||
* List of publicly accessible system events
|
||||
*/
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
@@ -69,54 +69,68 @@ return [
|
||||
],
|
||||
]
|
||||
],
|
||||
'collections' => [
|
||||
'$model' => Response::MODEL_COLLECTION,
|
||||
'databases' => [
|
||||
'$model' => Response::MODEL_DATABASE,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any collection event.',
|
||||
'documents' => [
|
||||
'$model' => Response::MODEL_DOCUMENT,
|
||||
'$description' => 'This event triggers on any database event.',
|
||||
'collections' => [
|
||||
'$model' => Response::MODEL_COLLECTION,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any documents event.',
|
||||
'$description' => 'This event triggers on any collection event.',
|
||||
'documents' => [
|
||||
'$model' => Response::MODEL_DOCUMENT,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any documents event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a document is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a document is deleted.'
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a document is updated.'
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
'$model' => Response::MODEL_INDEX,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any indexes event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when an index is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when an index is deleted.'
|
||||
]
|
||||
],
|
||||
'attributes' => [
|
||||
'$model' => Response::MODEL_ATTRIBUTE,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any attributes event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when an attribute is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when an attribute is deleted.'
|
||||
]
|
||||
],
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a document is created.',
|
||||
'$description' => 'This event triggers when a collection is created.'
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a document is deleted.'
|
||||
'$description' => 'This event triggers when a collection is deleted.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a document is updated.'
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
'$model' => Response::MODEL_INDEX,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any indexes event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when an index is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when an index is deleted.'
|
||||
]
|
||||
],
|
||||
'attributes' => [
|
||||
'$model' => Response::MODEL_ATTRIBUTE,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any attributes event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when an attribute is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when an attribute is deleted.'
|
||||
'$description' => 'This event triggers when a collection is updated.',
|
||||
]
|
||||
],
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a collection is created.'
|
||||
'$description' => 'This event triggers when a database is created.'
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a collection is deleted.',
|
||||
'$description' => 'This event triggers when a database is deleted.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a collection is updated.',
|
||||
'$description' => 'This event triggers when a database is updated.',
|
||||
]
|
||||
],
|
||||
'buckets' => [
|
||||
|
||||
@@ -51,7 +51,7 @@ return [
|
||||
'or', // Oriya
|
||||
'tl', // Filipino
|
||||
'pl', // Polish
|
||||
'pt-br', // Portuguese - Brazil
|
||||
'pt-br', // Portuguese - Brazil
|
||||
'pt-pt', // Portuguese - Portugal
|
||||
'pa', // Punjabi
|
||||
'ro', // Romanian
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"emails.certificate.subject": "Certificate failure for %s",
|
||||
"emails.certificate.hello": "Hello",
|
||||
"emails.certificate.body": "Certificate for your domain '{{domain}}' could not be generated. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}",
|
||||
"emails.certificate.footer": "Your previous certificate willl be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.",
|
||||
"emails.certificate.footer": "Your previous certificate will be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.",
|
||||
"emails.certificate.thanks": "Thanks",
|
||||
"emails.certificate.signature": "{{project}} team",
|
||||
"locale.country.unknown": "Unknown",
|
||||
@@ -235,4 +235,4 @@
|
||||
"continents.na": "North America",
|
||||
"continents.oc": "Oceania",
|
||||
"continents.sa": "South America"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"emails.sender": "Équipe %s",
|
||||
"emails.verification.subject": "Vérification du compte",
|
||||
"emails.verification.hello": "Bonjour {{name}}",
|
||||
"emails.verification.body": "Suivez ce lien pour vérifier votre adresse mail.",
|
||||
"emails.verification.footer": "Si vous n'avez pas demandé à vérifier cette adresse mail, vous pouvez ignorer ce message.",
|
||||
"emails.verification.body": "Suivez ce lien pour vérifier votre adresse e-mail.",
|
||||
"emails.verification.footer": "Si vous n'avez pas demandé à vérifier cette adresse, vous pouvez ignorer ce message.",
|
||||
"emails.verification.thanks": "Merci",
|
||||
"emails.verification.signature": "Équipe {{project}}",
|
||||
"emails.magicSession.subject": "Connexion",
|
||||
@@ -14,19 +14,25 @@
|
||||
"emails.magicSession.body": "Suivez ce lien pour vous connecter.",
|
||||
"emails.magicSession.footer": "Si vous n'avez pas demandé à vous connecter en utilisant cet e-mail, vous pouvez ignorer ce message.",
|
||||
"emails.magicSession.thanks": "Merci",
|
||||
"emails.magicSession.signature": "Réinitialisation du mot de passe",
|
||||
"emails.recovery.subject": "Bonjour {{name}}",
|
||||
"emails.magicSession.signature": "L'équipe {{project}}",
|
||||
"emails.recovery.subject": "Réinitialisation du mot de passe",
|
||||
"emails.recovery.hello": "Bonjour {{name}}",
|
||||
"emails.recovery.body": "Suivez ce lien pour réinitialiser votre mot de passe de {{projet}}.",
|
||||
"emails.recovery.body": "Suivez ce lien pour réinitialiser votre mot de passe pour {{projet}}.",
|
||||
"emails.recovery.footer": "Si vous n'avez pas demandé à réinitialiser votre mot de passe, vous pouvez ignorer ce message.",
|
||||
"emails.recovery.thanks": "Merci",
|
||||
"emails.recovery.signature": "Équipe {{project}}",
|
||||
"emails.recovery.signature": "L'équipe {{project}}",
|
||||
"emails.invitation.subject": "Invitation à l'équipe %s de %s",
|
||||
"emails.invitation.hello": "Bonjour",
|
||||
"emails.invitation.body": "Ce mail vous a été envoyé parce que {{owner}} voulait vous inviter à devenir membre de l'équipe {{team}} de {{project}}.",
|
||||
"emails.invitation.body": "Cet e-mail vous a été envoyé parce que {{owner}} souhaite vous inviter à devenir membre de l'équipe {{team}} pour {{project}}.",
|
||||
"emails.invitation.footer": "Si vous n'êtes pas intéressé, vous pouvez ignorer ce message.",
|
||||
"emails.invitation.thanks": "Merci",
|
||||
"emails.invitation.signature": "Équipe {{project}}",
|
||||
"emails.invitation.signature": "L'équipe {{project}}",
|
||||
"emails.certificate.subject": "Échec du certificat pour %s",
|
||||
"emails.certificate.hello": "Bonjour",
|
||||
"emails.certificate.body": "Le certificate pour votre domaine '{{domain}}' n'a pas pu être généré. Ceci est la tentative {{tentative}} et l'échec a été causé par : {{erreur}}",
|
||||
"emails.certificate.footer": "Votre certificat précédent sera valide pendant 30 jours à compter de la première défaillance. Nous vous recommandons fortement d'enquêter sur ce cas, sinon votre domaine se retrouvera sans communication SSL valide.",
|
||||
"emails.certificate.thanks": "Merci",
|
||||
"emails.certificate.signature": "L'équipe {{project}}",
|
||||
"locale.country.unknown": "Inconnu",
|
||||
"countries.af": "Afghanistan",
|
||||
"countries.ao": "Angola",
|
||||
@@ -229,4 +235,4 @@
|
||||
"continents.na": "Amérique du Nord",
|
||||
"continents.oc": "Océanie",
|
||||
"continents.sa": "Amérique du Sud"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,25 +6,25 @@
|
||||
"emails.verification.subject": "अकाउंट वेरिफिकेशन ",
|
||||
"emails.verification.hello": "नमस्ते {{name}}",
|
||||
"emails.verification.body": "इस लिंक के माध्यम से अपने ईमेल को सत्यापित कीजिये।",
|
||||
"emails.verification.footer": "यदि आपने इस पते को सत्यापित नहीं करना चाहते है, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.verification.footer": "यदि आप इस पते को सत्यापित नहीं करना चाहते हैं, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.verification.thanks": "धन्यवाद",
|
||||
"emails.verification.signature": "{{project}} टीम",
|
||||
"emails.magicSession.subject": "लॉग इन",
|
||||
"emails.magicSession.hello": "नमस्ते,",
|
||||
"emails.magicSession.body": "इस लिंक के माध्यम से लॉग-इन करें।",
|
||||
"emails.magicSession.footer": "यदि आप इस ईमेल द्वारा लॉगिन नहीं करना चाहते है, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.magicSession.footer": "यदि आप इस ईमेल द्वारा लॉगिन नहीं करना चाहते हैं, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.magicSession.thanks": "धन्यवाद",
|
||||
"emails.magicSession.signature": "{{project}} टीम",
|
||||
"emails.recovery.subject": "पासवर्ड रीसेट",
|
||||
"emails.recovery.hello": "नमस्ते {{name}}",
|
||||
"emails.recovery.body": "इस लिंक के माध्यम से अपना {{project}} पासवर्ड रीसेट करें।",
|
||||
"emails.recovery.footer": "यदि आप अपना पासवर्ड रिसेट नहीं करना चाहते है, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.recovery.footer": "यदि आप अपना पासवर्ड रीसेट नहीं करना चाहते हैं, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.recovery.thanks": "धन्यवाद",
|
||||
"emails.recovery.signature": "{{project}} टीम",
|
||||
"emails.invitation.subject": "%s टीम का यहाँ %s पर आमंत्रण",
|
||||
"emails.invitation.hello": "नमस्ते",
|
||||
"emails.invitation.body": "यह मेल आपको इसलिए भेजा गया था क्योंकि {{owner}} आपको {{team}} टीम का सदस्य बनाना चाहते थे, जो {{project}} से जुड़ा हुआ है।",
|
||||
"emails.invitation.footer": "यदि आप इसमे रूचि नहीं रखते, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.invitation.body": "यह मेल आपको इसलिए भेजा गया है क्योंकि {{owner}} आपको {{team}} टीम का सदस्य बनाना चाहते है, जो {{project}} से जुड़ा हुआ है।",
|
||||
"emails.invitation.footer": "यदि आप इसमें रूचि नहीं रखते, तो आप इस संदेश को नज़रअंदाज़ कर सकते हैं।",
|
||||
"emails.invitation.thanks": "धन्यवाद",
|
||||
"emails.invitation.signature": "{{project}} टीम",
|
||||
"locale.country.unknown": "अज्ञात",
|
||||
@@ -35,7 +35,7 @@
|
||||
"countries.ae": "संयुक्त अरब अमीरात",
|
||||
"countries.ar": "अर्जेंटीना",
|
||||
"countries.am": "आर्मीनिया",
|
||||
"countries.ag": "अंटीगुआ और बारबूडा",
|
||||
"countries.ag": "एंटीगुआ और बारबूडा",
|
||||
"countries.au": "ऑस्ट्रेलिया",
|
||||
"countries.at": "ऑस्ट्रिया",
|
||||
"countries.az": "अज़रबैजान",
|
||||
@@ -46,7 +46,7 @@
|
||||
"countries.bd": "बांग्लादेश",
|
||||
"countries.bg": "बुल्गारिया",
|
||||
"countries.bh": "बहरीन",
|
||||
"countries.bs": "बहामास",
|
||||
"countries.bs": "द बाहामास",
|
||||
"countries.ba": "बॉस्निया और हर्ज़ेगोविना",
|
||||
"countries.by": "बेलारूस",
|
||||
"countries.bz": "बेलीज़",
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
"emails.recovery.subject": "രഹസ്യവാക്ക് പുനക്രമീകരണം",
|
||||
"emails.recovery.hello": "നമസ്കാരം {{name}}",
|
||||
"emails.recovery.body": "നിങ്ങളുടെ {{Project}} രഹസ്യവാക്ക് പുനക്രമീകരിക്കുന്നതിന് ഈ ലിങ്ക് പിന്തുടരുക.",
|
||||
"emails.recovery.footer": "നിങ്ങളുടെ പാസ്വേഡ് പുനക്രമീകരിക്കാന് നിങ്ങൾ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.recovery.footer": "നിങ്ങളുടെ രഹസ്യവാക്ക് പുനക്രമീകരിക്കാന് നിങ്ങൾ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.recovery.thanks": "നന്ദി",
|
||||
"emails.recovery.signature": "{{project}} ടീം",
|
||||
"emails.invitation.subject": "%s -ലെ %s ടീമിലേക്കുള്ള ക്ഷണം",
|
||||
"emails.invitation.hello": "നമസ്കാരം",
|
||||
"emails.invitation.body": "നിങ്ങളെ {{project}} -ലെ {{team}} ടീമിലെ അംഗമാകുവാന് ക്ഷണിക്കാൻ {{owner}} ആഗ്രഹിക്കുതിനാലാണ് ഈ മെയിൽ നിങ്ങൾക്ക് അയക്കുന്നത്.",
|
||||
"emails.invitation.body": "നിങ്ങളെ {{project}} -ലെ {{team}} ടീമിലെ അംഗമാകുവാന് ക്ഷണിക്കാൻ {{owner}} ആഗ്രഹിക്കുന്നതിനാലാണ് ഈ മെയിൽ നിങ്ങൾക്ക് അയക്കുന്നത്.",
|
||||
"emails.invitation.footer": "നിങ്ങൾക്ക് താൽപ്പര്യമില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.invitation.thanks": "നന്ദി",
|
||||
"emails.invitation.signature": "{{project}} ടീം",
|
||||
@@ -229,4 +229,4 @@
|
||||
"continents.na": "വടക്കേ അമേരിക്ക",
|
||||
"continents.oc": "ഓഷ്യാനിയ",
|
||||
"continents.sa": "തെക്കേ അമേരിക്ക"
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -15,7 +15,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '8.0.0',
|
||||
'version' => '10.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -63,7 +63,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '5.0.0',
|
||||
'version' => '8.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -81,11 +81,11 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '0.5.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
@@ -116,11 +116,11 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '0.6.1',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
@@ -162,7 +162,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Console',
|
||||
'version' => '5.0.0',
|
||||
'version' => '7.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
'package' => '',
|
||||
'enabled' => true,
|
||||
@@ -180,7 +180,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '0.17.0',
|
||||
'version' => '1.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -208,7 +208,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '6.0.0',
|
||||
'version' => '8.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -226,7 +226,7 @@ return [
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '4.0.0',
|
||||
'version' => '6.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -244,7 +244,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '5.0.0',
|
||||
'version' => '7.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -262,7 +262,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '0.9.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -280,7 +280,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '5.0.0',
|
||||
'version' => '7.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -298,7 +298,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.1.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => '',
|
||||
'enabled' => false,
|
||||
@@ -316,7 +316,7 @@ return [
|
||||
[
|
||||
'key' => 'java',
|
||||
'name' => 'Java',
|
||||
'version' => '0.0.2',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-java',
|
||||
'package' => '',
|
||||
'enabled' => false,
|
||||
@@ -334,7 +334,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.4.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => false,
|
||||
@@ -352,7 +352,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '5.0.0',
|
||||
'version' => '7.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -370,11 +370,11 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '0.5.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
@@ -392,11 +392,11 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '0.5.0',
|
||||
'version' => '1.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
|
||||
+89
-28
@@ -17,7 +17,7 @@ return [ // Ordered by ABC.
|
||||
'icon' => 'icon-apple',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded.
|
||||
'form' => 'apple.phtml', // Preparation for adding ability to customized OAuth UI forms, currently handled hardcoded.
|
||||
'beta' => true,
|
||||
'mock' => false,
|
||||
],
|
||||
@@ -31,6 +31,26 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'authentik' => [
|
||||
'name' => 'Authentik',
|
||||
'developers' => 'https://goauthentik.io/docs/',
|
||||
'icon' => 'icon-authentik',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => 'authentik.phtml',
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'autodesk' => [
|
||||
'name' => 'Autodesk',
|
||||
'developers' => 'https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/',
|
||||
'icon' => 'icon-autodesk',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'bitbucket' => [
|
||||
'name' => 'BitBucket',
|
||||
'developers' => 'https://developer.atlassian.com/bitbucket',
|
||||
@@ -61,6 +81,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'dailymotion' => [
|
||||
'name' => 'Dailymotion',
|
||||
'developers' => 'https://developers.dailymotion.com/api/',
|
||||
'icon' => 'icon-dailymotion',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'discord' => [
|
||||
'name' => 'Discord',
|
||||
'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
|
||||
@@ -71,6 +101,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'disqus' => [
|
||||
'name' => 'Disqus',
|
||||
'developers' => 'https://disqus.com/api/docs/auth/',
|
||||
'icon' => 'icon-disqus',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'dropbox' => [
|
||||
'name' => 'Dropbox',
|
||||
'developers' => 'https://www.dropbox.com/developers/documentation',
|
||||
@@ -81,6 +121,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'etsy' => [
|
||||
'name' => 'Etsy',
|
||||
'developers' => 'https://developers.etsy.com/',
|
||||
'icon' => 'icon-etsy',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'facebook' => [
|
||||
'name' => 'Facebook',
|
||||
'developers' => 'https://developers.facebook.com/',
|
||||
@@ -107,7 +157,7 @@ return [ // Ordered by ABC.
|
||||
'icon' => 'icon-gitlab',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'form' => 'gitlab.phtml',
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
@@ -181,6 +231,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'podio' => [
|
||||
'name' => 'Podio',
|
||||
'developers' => 'https://developers.podio.com/doc/oauth-authorization',
|
||||
'icon' => 'icon-podio',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'salesforce' => [
|
||||
'name' => 'Salesforce',
|
||||
'developers' => 'https://developer.salesforce.com/docs/',
|
||||
@@ -211,6 +271,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'stripe' => [
|
||||
'name' => 'Stripe',
|
||||
'developers' => 'https://stripe.com/docs/api',
|
||||
'icon' => 'icon-stripe',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'tradeshift' => [
|
||||
'name' => 'Tradeshift',
|
||||
'developers' => 'https://developers.tradeshift.com/docs/api',
|
||||
@@ -241,15 +311,15 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'zoom' => [
|
||||
'name' => 'Zoom',
|
||||
'developers' => 'https://marketplace.zoom.us/docs/guides/auth/oauth/',
|
||||
'icon' => 'icon-zoom',
|
||||
'wordpress' => [
|
||||
'name' => 'WordPress',
|
||||
'developers' => 'https://developer.wordpress.com/docs/oauth2/',
|
||||
'icon' => 'icon-wordpress',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'yahoo' => [
|
||||
'name' => 'Yahoo',
|
||||
@@ -263,7 +333,7 @@ return [ // Ordered by ABC.
|
||||
],
|
||||
'yammer' => [
|
||||
'name' => 'Yammer',
|
||||
'developers' => 'https://developer.yammer.com/docs/oauth-2',
|
||||
'developers' => 'https://docs.microsoft.com/en-us/rest/api/yammer/oauth-2/',
|
||||
'icon' => 'icon-yammer',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
@@ -281,6 +351,16 @@ return [ // Ordered by ABC.
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'zoom' => [
|
||||
'name' => 'Zoom',
|
||||
'developers' => 'https://marketplace.zoom.us/docs/guides/auth/oauth/',
|
||||
'icon' => 'icon-zoom',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
// 'instagram' => [
|
||||
// 'name' => 'Instagram',
|
||||
// 'developers' => 'https://www.instagram.com/developer/',
|
||||
@@ -297,26 +377,7 @@ return [ // Ordered by ABC.
|
||||
// 'beta' => false,
|
||||
// 'mock' => false,
|
||||
// ],
|
||||
'wordpress' => [
|
||||
'name' => 'WordPress',
|
||||
'developers' => 'https://developer.wordpress.com/docs/oauth2/',
|
||||
'icon' => 'icon-wordpress',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'stripe' => [
|
||||
'name' => 'Stripe',
|
||||
'developers' => 'https://stripe.com/docs/api',
|
||||
'icon' => 'icon-stripe',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
|
||||
// Keep Last
|
||||
'mock' => [
|
||||
'name' => 'Mock',
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'name' => 'Default',
|
||||
'default' => true,
|
||||
'disabled' => false,
|
||||
]
|
||||
];
|
||||
+11
-6
@@ -32,6 +32,8 @@ $admins = [
|
||||
'buckets.write',
|
||||
'users.read',
|
||||
'users.write',
|
||||
'databases.read',
|
||||
'databases.write',
|
||||
'collections.read',
|
||||
'collections.write',
|
||||
'platforms.read',
|
||||
@@ -50,20 +52,23 @@ $admins = [
|
||||
];
|
||||
|
||||
return [
|
||||
Auth::USER_ROLE_GUEST => [
|
||||
'label' => 'Guest',
|
||||
Auth::USER_ROLE_GUESTS => [
|
||||
'label' => 'Guests',
|
||||
'scopes' => [
|
||||
'public',
|
||||
'home',
|
||||
'console',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'execution.write',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_MEMBER => [
|
||||
'label' => 'Member',
|
||||
Auth::USER_ROLE_USERS => [
|
||||
'label' => 'Users',
|
||||
'scopes' => \array_merge($member, []),
|
||||
],
|
||||
Auth::USER_ROLE_ADMIN => [
|
||||
@@ -78,8 +83,8 @@ return [
|
||||
'label' => 'Owner',
|
||||
'scopes' => \array_merge($member, $admins, []),
|
||||
],
|
||||
Auth::USER_ROLE_APP => [
|
||||
'label' => 'Application',
|
||||
Auth::USER_ROLE_APPS => [
|
||||
'label' => 'Applications',
|
||||
'scopes' => ['health.read'],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
/**
|
||||
* List of Appwrite Cloud Functions supported runtimes
|
||||
*/
|
||||
$runtimes = new Runtimes('v1');
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
$runtimes = new Runtimes('v2');
|
||||
|
||||
$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES'));
|
||||
|
||||
$runtimes = $runtimes->getAll(true, $allowList);
|
||||
|
||||
return $runtimes;
|
||||
return $runtimes;
|
||||
|
||||
@@ -13,6 +13,12 @@ return [ // List of publicly visible scopes
|
||||
'teams.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s teams',
|
||||
],
|
||||
'databases.read' => [
|
||||
'description' => 'Access to read your project\'s databases',
|
||||
],
|
||||
'databases.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s databases',
|
||||
],
|
||||
'collections.read' => [
|
||||
'description' => 'Access to read your project\'s database collections',
|
||||
],
|
||||
@@ -70,4 +76,4 @@ return [ // List of publicly visible scopes
|
||||
'health.read' => [
|
||||
'description' => 'Access to read your project\'s health status',
|
||||
],
|
||||
];;
|
||||
];
|
||||
|
||||
+25
-9
@@ -43,7 +43,7 @@ return [
|
||||
'avatars' => [
|
||||
'key' => 'avatars',
|
||||
'name' => 'Avatars',
|
||||
'subtitle'=> 'The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.',
|
||||
'subtitle' => 'The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.',
|
||||
'description' => '/docs/services/avatars.md',
|
||||
'controller' => 'api/avatars.php',
|
||||
'sdk' => true,
|
||||
@@ -53,18 +53,21 @@ return [
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/avatars.png',
|
||||
],
|
||||
'database' => [
|
||||
'key' => 'database',
|
||||
'name' => 'Database',
|
||||
'subtitle' => 'The Database service allows you to create structured collections of documents, query and filter lists of documents',
|
||||
'description' => '/docs/services/database.md',
|
||||
'controller' => 'api/database.php',
|
||||
'databases' => [
|
||||
'key' => 'databases',
|
||||
'name' => 'Databases',
|
||||
'subtitle' => 'The Databases service allows you to create structured collections of documents, query and filter lists of documents',
|
||||
'description' => '/docs/services/databases.md',
|
||||
'controller' => 'api/databases.php',
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => 'https://appwrite.io/docs/client/database',
|
||||
'docsUrl' => 'https://appwrite.io/docs/client/databases',
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/database.png',
|
||||
'icon' => '/images/services/databases.png',
|
||||
'globalAttributes' => [
|
||||
'databaseId'
|
||||
]
|
||||
],
|
||||
'locale' => [
|
||||
'key' => 'locale',
|
||||
@@ -105,6 +108,19 @@ return [
|
||||
'optional' => false,
|
||||
'icon' => '',
|
||||
],
|
||||
'project' => [
|
||||
'key' => 'project',
|
||||
'name' => 'Project',
|
||||
'subtitle' => 'The Project service allows you to manage all the projects in your Appwrite server.',
|
||||
'description' => '',
|
||||
'controller' => 'api/project.php',
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => '',
|
||||
'tests' => false,
|
||||
'optional' => false,
|
||||
'icon' => '',
|
||||
],
|
||||
'storage' => [
|
||||
'key' => 'storage',
|
||||
'name' => 'Storage',
|
||||
|
||||
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
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
@@ -5,4 +5,4 @@ return [ // Accepted inputs files
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
];
|
||||
];
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<?php
|
||||
|
||||
return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
'default' => __DIR__.'/logos/none.png',
|
||||
'default_image' => __DIR__.'/logos/image.png',
|
||||
|
||||
'default' => __DIR__ . '/logos/none.png',
|
||||
'default_image' => __DIR__ . '/logos/image.png',
|
||||
|
||||
// Video Files
|
||||
'video/mp4' => __DIR__.'/logos/video.png',
|
||||
'video/x-flv' => __DIR__.'/logos/video.png',
|
||||
'application/x-mpegURL' => __DIR__.'/logos/video.png',
|
||||
'video/MP2T' => __DIR__.'/logos/video.png',
|
||||
'video/3gpp' => __DIR__.'/logos/video.png',
|
||||
'video/quicktime' => __DIR__.'/logos/video.png',
|
||||
'video/x-msvideo' => __DIR__.'/logos/video.png',
|
||||
'video/x-ms-wmv' => __DIR__.'/logos/video.png',
|
||||
|
||||
'video/mp4' => __DIR__ . '/logos/video.png',
|
||||
'video/x-flv' => __DIR__ . '/logos/video.png',
|
||||
'video/webm' => __DIR__ . '/logos/video.png',
|
||||
'application/x-mpegURL' => __DIR__ . '/logos/video.png',
|
||||
'video/MP2T' => __DIR__ . '/logos/video.png',
|
||||
'video/3gpp' => __DIR__ . '/logos/video.png',
|
||||
'video/quicktime' => __DIR__ . '/logos/video.png',
|
||||
'video/x-msvideo' => __DIR__ . '/logos/video.png',
|
||||
'video/x-ms-wmv' => __DIR__ . '/logos/video.png',
|
||||
|
||||
|
||||
|
||||
// // Microsoft Word
|
||||
// 'application/msword' => __DIR__.'/logos/word.png',
|
||||
@@ -41,4 +45,4 @@ return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
|
||||
// Adobe PDF
|
||||
// 'application/pdf' => __DIR__.'/logos/pdf.png',
|
||||
];
|
||||
];
|
||||
|
||||
@@ -10,6 +10,7 @@ return [
|
||||
// Video Files
|
||||
'video/mp4',
|
||||
'video/x-flv',
|
||||
'video/webm',
|
||||
'application/x-mpegURL',
|
||||
'video/MP2T',
|
||||
'video/3gpp',
|
||||
@@ -30,7 +31,7 @@ return [
|
||||
'audio/ogg', // Ogg Vorbis RFC 5334
|
||||
'audio/vorbis', // Vorbis RFC 5215
|
||||
'audio/vnd.wav', // wav RFC 2361
|
||||
|
||||
|
||||
// Microsoft Word
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
@@ -61,4 +62,4 @@ return [
|
||||
|
||||
// Adobe PDF
|
||||
'application/pdf',
|
||||
];
|
||||
];
|
||||
|
||||
@@ -6,4 +6,4 @@ return [ // Accepted outputs files
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
'webp' => 'image/webp',
|
||||
];
|
||||
];
|
||||
|
||||
+270
-24
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
return [
|
||||
[
|
||||
'category' => 'General',
|
||||
@@ -67,7 +68,7 @@ return [
|
||||
'default' => 'localhost',
|
||||
'required' => true,
|
||||
'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.' . PHP_EOL . 'You can use the same value as used for the Appwrite hostname.',
|
||||
'filter' => ''
|
||||
'filter' => 'domainTarget'
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CONSOLE_WHITELIST_ROOT',
|
||||
@@ -97,7 +98,7 @@ return [
|
||||
// ],
|
||||
[
|
||||
'name' => '_APP_CONSOLE_WHITELIST_IPS',
|
||||
'description' => 'This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\n\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma.',
|
||||
'description' => "This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\n\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma.",
|
||||
'introduction' => '',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -169,13 +170,31 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_USAGE_AGGREGATION_INTERVAL',
|
||||
'description' => 'Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds.',
|
||||
'introduction' => '0.10.0',
|
||||
'description' => 'Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to Database from TimeSeries data. The default value is 30 seconds. Reintroduced in 1.1.0.',
|
||||
'introduction' => '1.1.0',
|
||||
'default' => '30',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_USAGE_TIMESERIES_INTERVAL',
|
||||
'description' => 'Deprecated since 1.1.0 use _APP_USAGE_AGGREGATION_INTERVAL instead.',
|
||||
'introduction' => '1.0.0',
|
||||
'default' => '30',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_USAGE_DATABASE_INTERVAL',
|
||||
'description' => 'Deprecated since 1.1.0 use _APP_USAGE_AGGREGATION_INTERVAL instead.',
|
||||
'introduction' => '1.0.0',
|
||||
'default' => '900',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_WORKER_PER_CORE',
|
||||
'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.',
|
||||
@@ -184,7 +203,16 @@ return [
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CONSOLE_INVITES',
|
||||
'description' => 'This option allows you to disable the invitation of new users to the Appwrite console. When enabled, console users are allowed to invite new users to a project. By default this option is enabled.',
|
||||
'introduction' => '1.2.0',
|
||||
'default' => 'enabled',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -287,6 +315,33 @@ return [
|
||||
'question' => '',
|
||||
'filter' => 'password'
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CONNECTIONS_MAX',
|
||||
'description' => 'MariaDB server maximum connections.',
|
||||
'introduction' => 'TBD',
|
||||
'default' => 251,
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
// [
|
||||
// 'name' => '_APP_CONNECTIONS_DB_PROJECT',
|
||||
// 'description' => 'A list of comma-separated key value pairs representing Project DBs where key is the database name and value is the DSN connection string.',
|
||||
// 'introduction' => 'TBD',
|
||||
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
|
||||
// 'required' => true,
|
||||
// 'question' => '',
|
||||
// 'filter' => ''
|
||||
// ],
|
||||
// [
|
||||
// 'name' => '_APP_CONNECTIONS_DB_CONSOLE',
|
||||
// 'description' => 'A key value pair representing the Console DB where key is the database name and value is the DSN connection string.',
|
||||
// 'introduction' => 'TBD',
|
||||
// 'default' => 'db_fra1_01=mysql://user:password@mariadb:3306/appwrite',
|
||||
// 'required' => true,
|
||||
// 'question' => '',
|
||||
// 'filter' => ''
|
||||
// ]
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -339,7 +394,7 @@ return [
|
||||
],
|
||||
[
|
||||
'category' => 'SMTP',
|
||||
'description' => 'Appwrite is using an SMTP server for emailing your projects users and server admins. The SMTP env vars are used to allow Appwrite server to connect to the SMTP container.\n\nIf running in production, it might be easier to use a 3rd party SMTP server as it might be a little more difficult to set up a production SMTP server that will not send all your emails into your user\'s SPAM folder.',
|
||||
'description' => "Appwrite is using an SMTP server for emailing your projects users and server admins. The SMTP env vars are used to allow Appwrite server to connect to the SMTP container.\n\nIf running in production, it might be easier to use a 3rd party SMTP server as it might be a little more difficult to set up a production SMTP server that will not send all your emails into your user\'s SPAM folder.",
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_SMTP_HOST',
|
||||
@@ -388,6 +443,30 @@ return [
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'category' => 'Phone',
|
||||
'description' => '',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_SMS_PROVIDER',
|
||||
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. \n\nAvailable providers are twilio, text-magic, telesign, msg91, and vonage.",
|
||||
'introduction' => '0.15.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_SMS_FROM',
|
||||
'description' => 'Phone number used for sending out messages. Must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789).',
|
||||
'introduction' => '0.15.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'category' => 'Storage',
|
||||
'description' => '',
|
||||
@@ -437,9 +516,17 @@ return [
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CONNECTIONS_STORAGE',
|
||||
'description' => 'A DSN representing the storage device to connect to. The DSN takes the following format <device>://<access_key>:<access_secret>@<host>:<port>/<bucket>?region=<region>. For example, for S3: \'s3://access_key:access_secret@host:port/bucket?region=us-east-1\'. To use the local filesystem, you can leave this variable empty. Available devices are local, s3, dospaces, linode, backblaze and wasabi.',
|
||||
'introduction' => '1.1.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DEVICE',
|
||||
'description' => 'Select default storage device. The default value is \'Local\'. List of supported adapters are \'Local\', \'S3\' and \'DOSpaces\'.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'Local',
|
||||
'required' => false,
|
||||
@@ -447,7 +534,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_S3_ACCESS_KEY',
|
||||
'description' => 'AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -455,7 +542,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_S3_SECRET',
|
||||
'description' => 'AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -463,7 +550,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_S3_REGION',
|
||||
'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-east-1',
|
||||
'required' => false,
|
||||
@@ -471,7 +558,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_S3_BUCKET',
|
||||
'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -479,7 +566,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DO_SPACES_ACCESS_KEY',
|
||||
'description' => 'DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -487,7 +574,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DO_SPACES_SECRET',
|
||||
'description' => 'DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -495,7 +582,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DO_SPACES_REGION',
|
||||
'description' => 'DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'us-east-1',
|
||||
'required' => false,
|
||||
@@ -503,12 +590,108 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_DO_SPACES_BUCKET',
|
||||
'description' => 'DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console.',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_BACKBLAZE_ACCESS_KEY',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_BACKBLAZE_SECRET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_BACKBLAZE_REGION',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => 'us-west-004',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_BACKBLAZE_BUCKET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_LINODE_ACCESS_KEY',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_LINODE_SECRET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_LINODE_REGION',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => 'eu-central-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_LINODE_BUCKET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_WASABI_ACCESS_KEY',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_WASABI_SECRET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_WASABI_REGION',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => 'eu-central-1',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
[
|
||||
'name' => '_APP_STORAGE_WASABI_BUCKET',
|
||||
'description' => 'Deprecated since 1.2.0. Use _APP_CONNECTIONS_STORAGE instead.',
|
||||
'introduction' => '0.14.2',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -526,7 +709,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_TIMEOUT',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds.',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.json.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '900',
|
||||
'required' => false,
|
||||
@@ -544,7 +727,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_CONTAINERS',
|
||||
'description' => 'The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10.',
|
||||
'description' => 'Deprecated since 1.2.0. Runtimes now timeout by inactivity using \'_APP_FUNCTIONS_INACTIVE_THRESHOLD\'.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '10',
|
||||
'required' => false,
|
||||
@@ -571,7 +754,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
|
||||
'description' => 'The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, swap memory limit will be disabled.',
|
||||
'description' => 'Deprecated since 1.2.0. High use of swap memory is not recommended to preserve harddrive health.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
@@ -580,7 +763,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_RUNTIMES',
|
||||
'description' => "This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\n\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\n\nCurrently, supported environments are: " . \implode(', ', \array_keys(Config::getParam('runtimes'))),
|
||||
'description' => "This option allows you to enable or disable runtime environments for cloud functions. Disable unused runtimes to save disk space.\n\nTo enable cloud function runtimes, pass a list of enabled environments separated by a comma.\n\nCurrently, supported environments are: " . \implode(', ', \array_keys(Config::getParam('runtimes'))),
|
||||
'introduction' => '0.8.0',
|
||||
'default' => 'node-16.0,php-8.0,python-3.9,ruby-3.0',
|
||||
'required' => false,
|
||||
@@ -625,7 +808,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_INACTIVE_THRESHOLD',
|
||||
'description' => 'The minimum time a function can be inactive before it\'s container is shutdown and put to sleep. The default value is 60 seconds',
|
||||
'description' => 'The minimum time a function can be inactive before it\'s container is shutdown and put to sleep. The default value is 60 seconds.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => '60',
|
||||
'required' => false,
|
||||
@@ -634,7 +817,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => 'DOCKERHUB_PULL_USERNAME',
|
||||
'description' => 'The username for hub.docker.com. This variable is used to pull images from hub.docker.com.',
|
||||
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_USERNAME\' instead!',
|
||||
'introduction' => '0.10.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -643,7 +826,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => 'DOCKERHUB_PULL_PASSWORD',
|
||||
'description' => 'The password for hub.docker.com. This variable is used to pull images from hub.docker.com.',
|
||||
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_PASSWORD\' instead!',
|
||||
'introduction' => '0.10.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -652,7 +835,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => 'DOCKERHUB_PULL_EMAIL',
|
||||
'description' => 'The email for hub.docker.com. This variable is used to pull images from hub.docker.com.',
|
||||
'description' => 'Deprecated since 1.2.0. Email is no longer needed.',
|
||||
'introduction' => '0.10.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
@@ -661,13 +844,49 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => 'OPEN_RUNTIMES_NETWORK',
|
||||
'description' => 'The docker network used for communication between the executor and runtimes. Change this if you have altered the default network names.',
|
||||
'description' => 'Deprecated with 1.2.0, use \'_APP_FUNCTIONS_RUNTIMES_NETWORK\' instead!',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 'appwrite_runtimes',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_RUNTIMES_NETWORK',
|
||||
'description' => 'The docker network used for communication between the executor and runtimes. Change this if you have altered the default network names.',
|
||||
'introduction' => '1.2.0',
|
||||
'default' => 'runtimes',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOCKER_HUB_USERNAME',
|
||||
'description' => 'The username for hub.docker.com. This variable is used to pull images from hub.docker.com.',
|
||||
'introduction' => '1.2.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOCKER_HUB_PASSWORD',
|
||||
'description' => 'The password for hub.docker.com. This variable is used to pull images from hub.docker.com.',
|
||||
'introduction' => '1.2.0',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_MAINTENANCE_INTERVAL',
|
||||
'description' => 'Interval how often executor checks for inactive runimes. The default value is 60 seconds.',
|
||||
'introduction' => '1.2.0',
|
||||
'default' => '60',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -683,6 +902,15 @@ return [
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days).',
|
||||
'introduction' => '1.0.0',
|
||||
'default' => '2592000',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_EXECUTION',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days).',
|
||||
@@ -709,6 +937,24 @@ return [
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_USAGE_HOURLY',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain hourly usage metrics. The default value is 8640000 seconds (100 days).',
|
||||
'introduction' => '',
|
||||
'default' => '8640000',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_SCHEDULES',
|
||||
'description' => 'Schedules deletion interval ( in seconds ) ',
|
||||
'introduction' => 'TBD',
|
||||
'default' => '86400',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
Submodule
+1
Submodule app/console added at b981302dee
+998
-635
File diff suppressed because it is too large
Load Diff
+864
-107
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+721
-322
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* 1. Map all objects, object-params, object-fields
|
||||
@@ -13,12 +10,15 @@ use Utopia\App;
|
||||
* 6. Write tests!
|
||||
*/
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
|
||||
App::post('/v1/graphql')
|
||||
->desc('GraphQL Endpoint')
|
||||
->groups(['api', 'graphql'])
|
||||
->label('scope', 'public')
|
||||
->action(
|
||||
function () {
|
||||
throw new Exception('GraphQL support is coming soon!', 502, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GraphQL support is coming soon!', 503);
|
||||
}
|
||||
);
|
||||
|
||||
+218
-49
@@ -5,11 +5,15 @@ use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::get('/v1/health')
|
||||
->desc('Get HTTP')
|
||||
@@ -26,6 +30,7 @@ App::get('/v1/health')
|
||||
->action(function (Response $response) {
|
||||
|
||||
$output = [
|
||||
'name' => 'http',
|
||||
'status' => 'pass',
|
||||
'ping' => 0
|
||||
];
|
||||
@@ -42,7 +47,6 @@ App::get('/v1/health/version')
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_VERSION)
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION);
|
||||
});
|
||||
|
||||
@@ -58,30 +62,50 @@ App::get('/v1/health/db')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||
->inject('response')
|
||||
->inject('utopia')
|
||||
->action(function (Response $response, App $utopia) {
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
$output = [];
|
||||
|
||||
try {
|
||||
$db = $utopia->getResource('db'); /* @var $db PDO */
|
||||
|
||||
// Run a small test to check the connection
|
||||
$statement = $db->prepare("SELECT 1;");
|
||||
|
||||
$statement->closeCursor();
|
||||
|
||||
$statement->execute();
|
||||
} catch (Exception $_e) {
|
||||
throw new Exception('Database is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = [
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
$configs = [
|
||||
'Console.DB' => Config::getParam('pools-console'),
|
||||
'Projects.DB' => Config::getParam('pools-database'),
|
||||
];
|
||||
|
||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
try {
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/health/cache')
|
||||
@@ -96,23 +120,163 @@ App::get('/v1/health/cache')
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||
->inject('response')
|
||||
->inject('utopia')
|
||||
->action(function (Response $response, App $utopia) {
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
$output = [];
|
||||
|
||||
$redis = $utopia->getResource('cache');
|
||||
|
||||
if (!$redis->ping(true)) {
|
||||
throw new Exception('Cache is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$output = [
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
$configs = [
|
||||
'Cache' => Config::getParam('pools-cache'),
|
||||
];
|
||||
|
||||
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
try {
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue')
|
||||
->desc('Get Queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getQueue')
|
||||
->label('sdk.description', '/docs/references/health/get-queue.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||
->inject('response')
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$output = [];
|
||||
|
||||
$configs = [
|
||||
'Queue' => Config::getParam('pools-queue'),
|
||||
];
|
||||
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
try {
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/health/pubsub')
|
||||
->desc('Get PubSub')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getPubSub')
|
||||
->label('sdk.description', '/docs/references/health/get-pubsub.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
|
||||
->inject('response')
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$output = [];
|
||||
|
||||
$configs = [
|
||||
'PubSub' => Config::getParam('pools-pubsub'),
|
||||
];
|
||||
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
try {
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
]), Response::MODEL_HEALTH_STATUS_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/health/time')
|
||||
@@ -141,7 +305,7 @@ App::get('/v1/health/time')
|
||||
\socket_connect($sock, $host, 123);
|
||||
|
||||
/* Send request */
|
||||
$msg = "\010".\str_repeat("\0", 47);
|
||||
$msg = "\010" . \str_repeat("\0", 47);
|
||||
|
||||
\socket_send($sock, $msg, \strlen($msg), 0);
|
||||
|
||||
@@ -160,7 +324,7 @@ App::get('/v1/health/time')
|
||||
$diff = ($timestamp - \time());
|
||||
|
||||
if ($diff > $gap || $diff < ($gap * -1)) {
|
||||
throw new Exception('Server time gaps detected', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Server time gaps detected');
|
||||
}
|
||||
|
||||
$output = [
|
||||
@@ -234,10 +398,11 @@ App::get('/v1/health/queue/functions')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
|
||||
->inject('queue')
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->dynamic(new Document([ 'size' => Resque::size(Event::FUNCTIONS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
|
||||
->action(function (Connection $queue, Response $response) {
|
||||
$client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue);
|
||||
$response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
|
||||
App::get('/v1/health/storage/local')
|
||||
@@ -256,20 +421,22 @@ App::get('/v1/health/storage/local')
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume) {
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
if (!\is_readable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not readable');
|
||||
}
|
||||
|
||||
if (!\is_writable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not writable');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,14 +471,16 @@ App::get('/v1/health/anti-virus')
|
||||
$output['status'] = 'disabled';
|
||||
$output['version'] = '';
|
||||
} else {
|
||||
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
|
||||
$antivirus = new Network(
|
||||
App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
|
||||
);
|
||||
|
||||
try {
|
||||
$output['version'] = @$antivirus->version();
|
||||
$output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail';
|
||||
} catch( \Exception $e) {
|
||||
throw new Exception('Antivirus is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
} catch (\Exception $e) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Antivirus is not available');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +507,7 @@ App::get('/v1/health/stats') // Currently only used internally
|
||||
$response
|
||||
->json([
|
||||
'storage' => [
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot().'/')),
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot() . '/')),
|
||||
'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()),
|
||||
],
|
||||
|
||||
@@ -40,7 +40,6 @@ App::get('/v1/locale')
|
||||
$output['countryCode'] = $record['country']['iso_code'];
|
||||
$output['country'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output['continent'] = $locale->getText('continents.' . strtolower($record['continent']['code']), $locale->getText('locale.country.unknown'));
|
||||
$output['continent'] = (isset($continents[$record['continent']['code']])) ? $continents[$record['continent']['code']] : $locale->getText('locale.country.unknown');
|
||||
$output['continentCode'] = $record['continent']['code'];
|
||||
$output['eu'] = (\in_array($record['country']['iso_code'], $eu)) ? true : false;
|
||||
|
||||
@@ -73,8 +72,8 @@ App::get('/v1/locale/countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountries')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries.md')
|
||||
->label('sdk.method', 'listCountries')
|
||||
->label('sdk.description', '/docs/references/locale/list-countries.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
|
||||
@@ -104,8 +103,8 @@ App::get('/v1/locale/countries/eu')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountriesEU')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
|
||||
->label('sdk.method', 'listCountriesEU')
|
||||
->label('sdk.description', '/docs/references/locale/list-countries-eu.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
|
||||
@@ -137,8 +136,8 @@ App::get('/v1/locale/countries/phones')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountriesPhones')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
|
||||
->label('sdk.method', 'listCountriesPhones')
|
||||
->label('sdk.description', '/docs/references/locale/list-countries-phones.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PHONE_LIST)
|
||||
@@ -169,8 +168,8 @@ App::get('/v1/locale/continents')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getContinents')
|
||||
->label('sdk.description', '/docs/references/locale/get-continents.md')
|
||||
->label('sdk.method', 'listContinents')
|
||||
->label('sdk.description', '/docs/references/locale/list-continents.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_CONTINENT_LIST)
|
||||
@@ -199,8 +198,8 @@ App::get('/v1/locale/currencies')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCurrencies')
|
||||
->label('sdk.description', '/docs/references/locale/get-currencies.md')
|
||||
->label('sdk.method', 'listCurrencies')
|
||||
->label('sdk.description', '/docs/references/locale/list-currencies.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_CURRENCY_LIST)
|
||||
@@ -220,8 +219,8 @@ App::get('/v1/locale/languages')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getLanguages')
|
||||
->label('sdk.description', '/docs/references/locale/get-languages.md')
|
||||
->label('sdk.method', 'listLanguages')
|
||||
->label('sdk.description', '/docs/references/locale/list-languages.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LANGUAGE_LIST)
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::get('/v1/project/usage')
|
||||
->desc('Get usage stats for a project')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'project')
|
||||
->label('sdk.method', 'getUsage')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_PROJECT)
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '1h',
|
||||
'limit' => 24,
|
||||
],
|
||||
'7d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 7,
|
||||
],
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
],
|
||||
'90d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 90,
|
||||
],
|
||||
];
|
||||
|
||||
$metrics = [
|
||||
'project.$all.network.requests',
|
||||
'project.$all.network.bandwidth',
|
||||
'project.$all.storage.size',
|
||||
'users.$all.count.total',
|
||||
'databases.$all.count.total',
|
||||
'documents.$all.count.total',
|
||||
'executions.$all.compute.total',
|
||||
'buckets.$all.count.total'
|
||||
];
|
||||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('period', [$period]),
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'1h' => 3600,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)),
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'requests' => $stats[$metrics[0]] ?? [],
|
||||
'network' => $stats[$metrics[1]] ?? [],
|
||||
'storage' => $stats[$metrics[2]] ?? [],
|
||||
'users' => $stats[$metrics[3]] ?? [],
|
||||
'databases' => $stats[$metrics[4]] ?? [],
|
||||
'documents' => $stats[$metrics[5]] ?? [],
|
||||
'executions' => $stats[$metrics[6]] ?? [],
|
||||
'buckets' => $stats[$metrics[7]] ?? [],
|
||||
]);
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
+397
-333
File diff suppressed because it is too large
Load Diff
+522
-545
File diff suppressed because it is too large
Load Diff
+231
-198
@@ -2,7 +2,6 @@
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Audit as EventAudit;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Mail;
|
||||
@@ -11,6 +10,11 @@ use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Teams;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -21,21 +25,25 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/teams')
|
||||
->desc('Create Team')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].create')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'team.create')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'create')
|
||||
@@ -43,41 +51,54 @@ App::post('/v1/teams')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM)
|
||||
->param('teamId', '', new CustomId(), 'Team ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('teamId', '', new CustomId(), 'Team ID. Choose your own unique ID or pass the string `ID.unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
|
||||
->param('roles', ['owner'], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', true)
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events, Event $audits) {
|
||||
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events) {
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
$teamId = $teamId == 'unique()' ? $dbForProject->getId() : $teamId;
|
||||
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
|
||||
$team = Authorization::skip(fn() => $dbForProject->createDocument('teams', new Document([
|
||||
'$id' => $teamId ,
|
||||
'$read' => ['team:'.$teamId],
|
||||
'$write' => ['team:'.$teamId .'/owner'],
|
||||
'$id' => $teamId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team($teamId)),
|
||||
Permission::update(Role::team($teamId, 'owner')),
|
||||
Permission::delete(Role::team($teamId, 'owner')),
|
||||
],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'dateCreated' => \time(),
|
||||
'search' => implode(' ', [$teamId, $name]),
|
||||
])));
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
|
||||
$membershipId = $dbForProject->getId();
|
||||
if (!\in_array('owner', $roles)) {
|
||||
$roles[] = 'owner';
|
||||
}
|
||||
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['user:'.$user->getId(), 'team:'.$team->getId()],
|
||||
'$write' => ['user:'.$user->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::read(Role::team($team->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::update(Role::team($team->getId(), 'owner')),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'roles' => $roles,
|
||||
'invited' => \time(),
|
||||
'joined' => \time(),
|
||||
'invited' => DateTime::now(),
|
||||
'joined' => DateTime::now(),
|
||||
'confirm' => true,
|
||||
'secret' => '',
|
||||
'search' => implode(' ', [$membershipId, $user->getId()])
|
||||
@@ -93,14 +114,9 @@ App::post('/v1/teams')
|
||||
$events->setParam('userId', $user->getId());
|
||||
}
|
||||
|
||||
$audits
|
||||
->setParam('event', 'teams.create')
|
||||
->setParam('resource', 'team/'.$teamId)
|
||||
->setParam('data', $team->getArrayCopy())
|
||||
;
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::get('/v1/teams')
|
||||
@@ -114,32 +130,37 @@ App::get('/v1/teams')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
|
||||
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of teams to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
|
||||
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorTeam = $dbForProject->getDocument('teams', $cursor);
|
||||
|
||||
if ($cursorTeam->isEmpty()) {
|
||||
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
$queries = Query::parseQueries($queries);
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
$results = $dbForProject->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection);
|
||||
$total = $dbForProject->count('teams', $queries, APP_LIMIT_COUNT);
|
||||
// Get cursor document if there was a cursor query
|
||||
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$teamId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Team '{$teamId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForProject->find('teams', $queries);
|
||||
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'teams' => $results,
|
||||
@@ -166,7 +187,7 @@ App::get('/v1/teams/:teamId')
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
@@ -177,6 +198,8 @@ App::put('/v1/teams/:teamId')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].update')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'team.update')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'update')
|
||||
@@ -189,22 +212,19 @@ App::put('/v1/teams/:teamId')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events, EventAudit $audits) {
|
||||
->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(),$team
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$teamId, $name]))
|
||||
);
|
||||
->setAttribute('search', implode(' ', [$teamId, $name])));
|
||||
|
||||
$events->setParam('teamId', $team->getId());
|
||||
$audits->setResource('team/' . $team->getId());
|
||||
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
@@ -214,6 +234,8 @@ App::delete('/v1/teams/:teamId')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].delete')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'team.delete')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'delete')
|
||||
@@ -225,28 +247,28 @@ App::delete('/v1/teams/:teamId')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('deletes')
|
||||
->inject('audits')
|
||||
->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes, EventAudit $audits) {
|
||||
->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find('memberships', [
|
||||
new Query('teamId', Query::TYPE_EQUAL, [$teamId]),
|
||||
], 2000, 0); // TODO fix members limit
|
||||
Query::equal('teamId', [$teamId]),
|
||||
Query::limit(2000), // TODO fix members limit
|
||||
]);
|
||||
|
||||
// TODO delete all members individually from the user object
|
||||
foreach ($memberships as $membership) {
|
||||
if (!$dbForProject->deleteDocument('memberships', $membership->getId())) {
|
||||
throw new Exception('Failed to remove membership for team from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership for team from DB');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('teams', $teamId)) {
|
||||
throw new Exception('Failed to remove team from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB');
|
||||
}
|
||||
|
||||
$deletes
|
||||
@@ -258,12 +280,6 @@ App::delete('/v1/teams/:teamId')
|
||||
->setPayload($response->output($team, Response::MODEL_TEAM))
|
||||
;
|
||||
|
||||
$audits
|
||||
->setParam('event', 'teams.delete')
|
||||
->setParam('resource', 'team/'.$teamId)
|
||||
->setParam('data', $team->getArrayCopy())
|
||||
;
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
@@ -273,6 +289,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].create')
|
||||
->label('scope', 'teams.write')
|
||||
->label('auth.type', 'invites')
|
||||
->label('audits.event', 'membership.create')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'createMembership')
|
||||
@@ -284,64 +303,68 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('email', '', new Email(), 'Email of the new team member.')
|
||||
->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](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
|
||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('audits')
|
||||
->inject('mails')
|
||||
->inject('events')
|
||||
->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, EventAudit $audits, Mail $mails, Event $events) {
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, Event $events) {
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$email = \strtolower($email);
|
||||
$name = (empty($name)) ? $email : $name;
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
$invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address
|
||||
|
||||
if (empty($invitee)) { // Create new user if no user with same email found
|
||||
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
||||
$total = $dbForProject->count('users', [], APP_LIMIT_USERS);
|
||||
|
||||
if($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
if ($total >= $limit) {
|
||||
throw new Exception(Exception::USER_COUNT_EXCEEDED, 'Project registration is restricted. Contact your administrator for more information.');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = $dbForProject->getId();
|
||||
$userId = ID::unique();
|
||||
$invitee = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['user:'.$userId, 'role:all'],
|
||||
'$write' => ['user:'.$userId],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
*/
|
||||
'passwordUpdate' => 0,
|
||||
'registration' => \time(),
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
@@ -351,28 +374,35 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
'search' => implode(' ', [$userId, $email, $name])
|
||||
])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS);
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');;
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to send invitations for this team', 401, Exception::USER_UNAUTHORIZED);
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team');
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$membershipId = $dbForProject->getId();
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::user($invitee->getId())),
|
||||
Permission::update(Role::team($team->getId(), 'owner')),
|
||||
Permission::delete(Role::user($invitee->getId())),
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $invitee->getId(),
|
||||
'userInternalId' => $invitee->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'roles' => $roles,
|
||||
'invited' => \time(),
|
||||
'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
|
||||
'invited' => DateTime::now(),
|
||||
'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null,
|
||||
'confirm' => ($isPrivilegedUser || $isAppUser),
|
||||
'secret' => Auth::hash($secret),
|
||||
'search' => implode(' ', [$membershipId, $invitee->getId()])
|
||||
@@ -382,7 +412,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
try {
|
||||
$membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
}
|
||||
$team->setAttribute('total', $team->getAttribute('total', 0) + 1);
|
||||
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
@@ -392,7 +422,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
try {
|
||||
$membership = $dbForProject->createDocument('memberships', $membership);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,84 +443,86 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
;
|
||||
}
|
||||
|
||||
$audits
|
||||
->setResource('team/'.$teamId)
|
||||
;
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
;
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
, Response::MODEL_MEMBERSHIP);
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $invitee->getAttribute('name'))
|
||||
->setAttribute('userEmail', $invitee->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/memberships')
|
||||
->desc('Get Team Memberships')
|
||||
->desc('List Team Memberships')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'getMemberships')
|
||||
->label('sdk.description', '/docs/references/teams/get-team-members.md')
|
||||
->label('sdk.method', 'listMemberships')
|
||||
->label('sdk.description', '/docs/references/teams/list-team-members.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of memberships to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $teamId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
|
||||
->action(function (string $teamId, array $queries, string $search, Response $response, Database $dbForProject) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorMembership = $dbForProject->getDocument('memberships', $cursor);
|
||||
|
||||
if ($cursorMembership->isEmpty()) {
|
||||
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
$queries = [new Query('teamId', Query::TYPE_EQUAL, [$teamId])];
|
||||
$queries = Query::parseQueries($queries);
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('teamId', [$teamId]);
|
||||
|
||||
// Get cursor document if there was a cursor query
|
||||
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$membershipId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Membership '{$membershipId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
limit: $limit,
|
||||
offset: $offset,
|
||||
orderTypes: [$orderType],
|
||||
cursor: $cursorMembership ?? null,
|
||||
cursorDirection: $cursorDirection
|
||||
);
|
||||
|
||||
$total = $dbForProject->count(
|
||||
collection:'memberships',
|
||||
queries: $queries,
|
||||
collection: 'memberships',
|
||||
queries: $filterQueries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
|
||||
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
$memberships = array_map(function($membership) use ($dbForProject, $team) {
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$membership
|
||||
@@ -518,7 +550,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('sdk.description', '/docs/references/teams/get-team-member.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
|
||||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->inject('response')
|
||||
@@ -528,13 +560,13 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
if ($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
@@ -545,7 +577,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
;
|
||||
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP );
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
@@ -553,6 +585,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].update')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'membership.update')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'updateMembershipRoles')
|
||||
@@ -567,31 +601,30 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $events) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$profile = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');;
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to modify roles', 401, Exception::USER_UNAUTHORIZED);
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,8 +638,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
*/
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$audits->setResource('team/' . $teamId);
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId());
|
||||
@@ -625,6 +656,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
|
||||
->label('scope', 'public')
|
||||
->label('audits.event', 'membership.update')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'updateMembershipStatus')
|
||||
@@ -640,34 +674,34 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('geodb')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Reader $geodb, EventAudit $audits, Event $events) {
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $events) {
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404, Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
$team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId));
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
|
||||
throw new Exception('Secret key not valid', 401, Exception::TEAM_INVALID_SECRET);
|
||||
throw new Exception(Exception::TEAM_INVALID_SECRET);
|
||||
}
|
||||
|
||||
if ($userId !== $membership->getAttribute('userId')) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
|
||||
}
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
@@ -675,50 +709,51 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('userId') !== $user->getId()) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('confirm') === true) {
|
||||
throw new Exception('Membership already confirmed', 409);
|
||||
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
|
||||
}
|
||||
|
||||
$membership // Attach user to team
|
||||
->setAttribute('joined', \time())
|
||||
->setAttribute('joined', DateTime::now())
|
||||
->setAttribute('confirm', true)
|
||||
;
|
||||
|
||||
$user
|
||||
->setAttribute('emailVerification', true)
|
||||
;
|
||||
$user = Authorization::skip(fn() => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
|
||||
|
||||
// Log user in
|
||||
|
||||
Authorization::setRole('user:'.$user->getId());
|
||||
Authorization::setRole(Role::user($user->getId())->toString());
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $authDuration);
|
||||
$secret = Auth::tokenGenerator();
|
||||
$session = new Document(array_merge([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $user->getAttribute('email'),
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
|
||||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
->setAttribute('$permissions', [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
Authorization::setRole('user:'.$userId);
|
||||
Authorization::setRole(Role::user($userId)->toString());
|
||||
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
|
||||
@@ -726,8 +761,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
|
||||
|
||||
$audits->setResource('team/'.$teamId);
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
@@ -740,15 +773,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
}
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
;
|
||||
|
||||
$response->dynamic($membership
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
, Response::MODEL_MEMBERSHIP);
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
|
||||
App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
@@ -756,6 +791,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'membership.delete')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'deleteMembership')
|
||||
@@ -766,38 +803,37 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $events) {
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if ($membership->isEmpty()) {
|
||||
throw new Exception('Invite not found', 404, Exception::TEAM_INVITE_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_INVITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404);
|
||||
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->deleteDocument('memberships', $membership->getId());
|
||||
} catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB');
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
@@ -807,8 +843,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
}
|
||||
|
||||
$audits->setResource('team/'.$teamId);
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
@@ -829,28 +863,27 @@ App::get('/v1/teams/:teamId/logs')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('teamId', null, new UID(), 'Team ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($teamId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$resource = 'team/'.$team->getId();
|
||||
$resource = 'team/' . $team->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
@@ -890,8 +923,8 @@ App::get('/v1/teams/:teamId/logs')
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
||||
+578
-303
File diff suppressed because it is too large
Load Diff
+509
-491
File diff suppressed because it is too large
Load Diff
+135
-86
@@ -5,13 +5,16 @@ global $utopia, $request, $response;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\App;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
App::get('/v1/mock/tests/foo')
|
||||
->desc('Get Foo')
|
||||
@@ -193,6 +196,35 @@ App::delete('/v1/mock/tests/bar')
|
||||
->action(function ($required, $default, $z) {
|
||||
});
|
||||
|
||||
/** Endpoint to test if required headers are sent from the SDK */
|
||||
App::get('/v1/mock/tests/general/headers')
|
||||
->desc('Get headers')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'general')
|
||||
->label('sdk.method', 'headers')
|
||||
->label('sdk.description', 'Return headers from the request')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$res = [
|
||||
'x-sdk-name' => $request->getHeader('x-sdk-name'),
|
||||
'x-sdk-platform' => $request->getHeader('x-sdk-platform'),
|
||||
'x-sdk-language' => $request->getHeader('x-sdk-language'),
|
||||
'x-sdk-version' => $request->getHeader('x-sdk-version'),
|
||||
];
|
||||
$res = array_map(function ($key, $value) {
|
||||
return $key . ': ' . $value;
|
||||
}, array_keys($res), $res);
|
||||
$res = implode("; ", $res);
|
||||
|
||||
$response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/download')
|
||||
->desc('Download File')
|
||||
->groups(['mock'])
|
||||
@@ -206,15 +238,14 @@ App::get('/v1/mock/tests/general/download')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setContentType('text/plain')
|
||||
->addHeader('Content-Disposition', 'attachment; filename="test.txt"')
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->send("Download test passed.")
|
||||
->send("GET:/v1/mock/tests/general/download:passed")
|
||||
;
|
||||
});
|
||||
|
||||
@@ -237,54 +268,52 @@ App::post('/v1/mock/tests/general/upload')
|
||||
->param('file', [], new File(), 'Sample file param', false)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($x, $y, $z, $file, $request, $response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Swoole\Response $response */
|
||||
|
||||
->action(function (string $x, int $y, array $z, mixed $file, Request $request, Response $response) {
|
||||
|
||||
$file = $request->getFiles('file');
|
||||
|
||||
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
|
||||
$chunkSize = 5*1024*1024; // 5MB
|
||||
$chunkSize = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
if(!empty($contentRange)) {
|
||||
if (!empty($contentRange)) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$size = $request->getContentRangeSize();
|
||||
$id = $request->getHeader('x-appwrite-id', '');
|
||||
$file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
if(is_null($start) || is_null($end) || is_null($size)) {
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
if (is_null($start) || is_null($end) || is_null($size)) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header');
|
||||
}
|
||||
|
||||
if($start > $end || $end > $size) {
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
if ($start > $end || $end > $size) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header');
|
||||
}
|
||||
|
||||
if($start === 0 && !empty($id)) {
|
||||
throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK);
|
||||
if ($start === 0 && !empty($id)) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'First chunked request cannot have id header');
|
||||
}
|
||||
|
||||
if($start !== 0 && $id !== 'newfileid') {
|
||||
throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK);
|
||||
if ($start !== 0 && $id !== 'newfileid') {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'All chunked request must have id header (except first)');
|
||||
}
|
||||
|
||||
if($end !== $size && $end-$start+1 !== $chunkSize) {
|
||||
throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK);
|
||||
if ($end !== $size && $end - $start + 1 !== $chunkSize) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB (except last chunk)');
|
||||
}
|
||||
|
||||
if ($end !== $size && $file['size'] !== $chunkSize) {
|
||||
throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($file['size'] > $chunkSize) {
|
||||
throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Wrong chunk size');
|
||||
}
|
||||
|
||||
if($end !== $size) {
|
||||
if ($file['size'] > $chunkSize) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB or less');
|
||||
}
|
||||
|
||||
if ($end !== $size) {
|
||||
$response->json([
|
||||
'$id'=> 'newfileid',
|
||||
'$id' => ID::custom('newfileid'),
|
||||
'chunksTotal' => $file['size'] / $chunkSize,
|
||||
'chunksUploaded' => $start / $chunkSize
|
||||
]);
|
||||
@@ -293,17 +322,17 @@ App::post('/v1/mock/tests/general/upload')
|
||||
$file['tmp_name'] = (\is_array($file['tmp_name'])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$file['name'] = (\is_array($file['name'])) ? $file['name'][0] : $file['name'];
|
||||
$file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
|
||||
if ($file['name'] !== 'file.png') {
|
||||
throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Wrong file name');
|
||||
}
|
||||
|
||||
|
||||
if ($file['size'] !== 38756) {
|
||||
throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size');
|
||||
}
|
||||
|
||||
if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
|
||||
throw new Exception('Wrong file uploaded', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Wrong file uploaded');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -321,8 +350,7 @@ App::get('/v1/mock/tests/general/redirect')
|
||||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->redirect('/v1/mock/tests/general/redirect/done');
|
||||
});
|
||||
@@ -356,9 +384,7 @@ App::get('/v1/mock/tests/general/set-cookie')
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->inject('request')
|
||||
->action(function ($response, $request) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
->action(function (Response $response, Request $request) {
|
||||
|
||||
$response->addCookie('cookieName', 'cookieValue', \time() + 31536000, '/', $request->getHostname(), true, true);
|
||||
});
|
||||
@@ -376,11 +402,10 @@ App::get('/v1/mock/tests/general/get-cookie')
|
||||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('request')
|
||||
->action(function ($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
->action(function (Request $request) {
|
||||
|
||||
if ($request->getCookie('cookieName', '') !== 'cookieValue') {
|
||||
throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Missing cookie value');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -396,12 +421,40 @@ App::get('/v1/mock/tests/general/empty')
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
/** Endpoint to test if required headers are sent from the SDK */
|
||||
App::get('/v1/mock/tests/general/headers')
|
||||
->desc('Get headers')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'general')
|
||||
->label('sdk.method', 'headers')
|
||||
->label('sdk.description', 'Return headers from the request')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (Request $request, Response $response) {
|
||||
$res = [
|
||||
'x-sdk-name' => $request->getHeader('x-sdk-name'),
|
||||
'x-sdk-platform' => $request->getHeader('x-sdk-platform'),
|
||||
'x-sdk-language' => $request->getHeader('x-sdk-language'),
|
||||
'x-sdk-version' => $request->getHeader('x-sdk-version'),
|
||||
];
|
||||
$res = array_map(function ($key, $value) {
|
||||
return $key . ': ' . $value;
|
||||
}, array_keys($res), $res);
|
||||
$res = implode("; ", $res);
|
||||
|
||||
$response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/400-error')
|
||||
->desc('400 Error')
|
||||
->groups(['mock'])
|
||||
@@ -415,7 +468,7 @@ App::get('/v1/mock/tests/general/400-error')
|
||||
->label('sdk.response.model', Response::MODEL_ERROR)
|
||||
->label('sdk.mock', true)
|
||||
->action(function () {
|
||||
throw new Exception('Mock 400 error', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Mock 400 error');
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/500-error')
|
||||
@@ -431,7 +484,7 @@ App::get('/v1/mock/tests/general/500-error')
|
||||
->label('sdk.response.model', Response::MODEL_ERROR)
|
||||
->label('sdk.mock', true)
|
||||
->action(function () {
|
||||
throw new Exception('Mock 500 error', 500, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Mock 500 error', 500);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/502-error')
|
||||
@@ -447,8 +500,7 @@ App::get('/v1/mock/tests/general/502-error')
|
||||
->label('sdk.response.model', Response::MODEL_ANY)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setStatusCode(502)
|
||||
@@ -467,10 +519,9 @@ App::get('/v1/mock/tests/general/oauth2')
|
||||
->param('scope', '', new Text(100), 'OAuth2 scope list.')
|
||||
->param('state', '', new Text(1024), 'OAuth2 state.')
|
||||
->inject('response')
|
||||
->action(function ($client_id, $redirectURI, $scope, $state, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $client_id, string $redirectURI, string $scope, string $state, Response $response) {
|
||||
|
||||
$response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/token')
|
||||
@@ -486,15 +537,14 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||
->param('code', '', new Text(100), 'OAuth2 state.', true)
|
||||
->param('refresh_token', '', new Text(100), 'OAuth2 refresh token.', true)
|
||||
->inject('response')
|
||||
->action(function ($client_id, $client_secret, $grantType, $redirectURI, $code, $refreshToken, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) {
|
||||
|
||||
if ($client_id != '1') {
|
||||
throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid client ID');
|
||||
}
|
||||
|
||||
if ($client_secret != '123456') {
|
||||
throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid client secret');
|
||||
}
|
||||
|
||||
$responseJson = [
|
||||
@@ -503,20 +553,20 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||
'expires_in' => 14400
|
||||
];
|
||||
|
||||
if($grantType === 'authorization_code') {
|
||||
if ($grantType === 'authorization_code') {
|
||||
if ($code !== 'abcdef') {
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid token');
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else if($grantType === 'refresh_token') {
|
||||
} elseif ($grantType === 'refresh_token') {
|
||||
if ($refreshToken !== 'tuvwxyz') {
|
||||
throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid refresh token');
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else {
|
||||
throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid grant type');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -527,11 +577,10 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
||||
->label('docs', false)
|
||||
->param('token', '', new Text(100), 'OAuth2 Access Token.')
|
||||
->inject('response')
|
||||
->action(function ($token, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $token, Response $response) {
|
||||
|
||||
if ($token != '123456') {
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Invalid token');
|
||||
}
|
||||
|
||||
$response->json([
|
||||
@@ -547,8 +596,7 @@ App::get('/v1/mock/tests/general/oauth2/success')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->json([
|
||||
'result' => 'success',
|
||||
@@ -561,8 +609,7 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_BAD_REQUEST)
|
||||
@@ -571,27 +618,29 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
]);
|
||||
});
|
||||
|
||||
App::shutdown(function($utopia, $response, $request) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
App::shutdown()
|
||||
->groups(['mock'])
|
||||
->inject('utopia')
|
||||
->inject('response')
|
||||
->inject('request')
|
||||
->action(function (App $utopia, Response $response, Request $request) {
|
||||
|
||||
$result = [];
|
||||
$route = $utopia->match($request);
|
||||
$path = APP_STORAGE_CACHE.'/tests.json';
|
||||
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
||||
|
||||
if (!\is_array($tests)) {
|
||||
throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK);
|
||||
}
|
||||
$result = [];
|
||||
$route = $utopia->match($request);
|
||||
$path = APP_STORAGE_CACHE . '/tests.json';
|
||||
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
||||
|
||||
$result[$route->getMethod() . ':' . $route->getPath()] = true;
|
||||
if (!\is_array($tests)) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Failed to read results', 500);
|
||||
}
|
||||
|
||||
$tests = \array_merge($tests, $result);
|
||||
$result[$route->getMethod() . ':' . $route->getPath()] = true;
|
||||
|
||||
if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) {
|
||||
throw new Exception('Failed to save results', 500, Exception::GENERAL_MOCK);
|
||||
}
|
||||
$tests = \array_merge($tests, $result);
|
||||
|
||||
$response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK);
|
||||
}, ['utopia', 'response', 'request'], 'mock');
|
||||
if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) {
|
||||
throw new Exception(Exception::GENERAL_MOCK, 'Failed to save results', 500);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK);
|
||||
});
|
||||
|
||||
+490
-237
@@ -1,270 +1,523 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Usage\Stats;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Utopia\App;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $mails, $usage, $deletes, $database, $dbForProject, $mode) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Mail $mails */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $deletes */
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $functions */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
|
||||
preg_match_all('/{(.*?)}/', $label, $matches);
|
||||
foreach ($matches[1] ?? [] as $pos => $match) {
|
||||
$find = $matches[0][$pos];
|
||||
$parts = explode('.', $match);
|
||||
|
||||
$route = $utopia->match($request);
|
||||
if (count($parts) !== 2) {
|
||||
throw new Exception('Too less or too many parts', 400, Exception::GENERAL_ARGUMENT_INVALID);
|
||||
}
|
||||
|
||||
if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
|
||||
throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN);
|
||||
$namespace = $parts[0] ?? '';
|
||||
$replace = $parts[1] ?? '';
|
||||
|
||||
$params = match ($namespace) {
|
||||
'user' => (array)$user,
|
||||
'request' => $requestParams,
|
||||
default => $responsePayload,
|
||||
};
|
||||
|
||||
if (array_key_exists($replace, $params)) {
|
||||
$label = \str_replace($find, $params[$replace], $label);
|
||||
}
|
||||
}
|
||||
return $label;
|
||||
};
|
||||
|
||||
$databaseListener = function (string $event, Document $document, Stats $usage) {
|
||||
$multiplier = 1;
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$multiplier = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Abuse Check
|
||||
*/
|
||||
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
|
||||
$timeLimitArray = [];
|
||||
$collection = $document->getCollection();
|
||||
switch ($collection) {
|
||||
case 'users':
|
||||
$usage->setParam('users.{scope}.count.total', 1 * $multiplier);
|
||||
break;
|
||||
case 'databases':
|
||||
$usage->setParam('databases.{scope}.count.total', 1 * $multiplier);
|
||||
break;
|
||||
case 'buckets':
|
||||
$usage->setParam('buckets.{scope}.count.total', 1 * $multiplier);
|
||||
break;
|
||||
case 'deployments':
|
||||
$usage->setParam('deployments.{scope}.storage.size', $document->getAttribute('size') * $multiplier);
|
||||
break;
|
||||
default:
|
||||
if (strpos($collection, 'bucket_') === 0) {
|
||||
$usage
|
||||
->setParam('bucketId', $document->getAttribute('bucketId'))
|
||||
->setParam('files.{scope}.storage.size', $document->getAttribute('sizeOriginal') * $multiplier)
|
||||
->setParam('files.{scope}.count.total', 1 * $multiplier);
|
||||
} elseif (strpos($collection, 'database_') === 0) {
|
||||
$usage
|
||||
->setParam('databaseId', $document->getAttribute('databaseId'));
|
||||
if (strpos($collection, '_collection_') !== false) {
|
||||
$usage
|
||||
->setParam('collectionId', $document->getAttribute('$collectionId'))
|
||||
->setParam('documents.{scope}.count.total', 1 * $multiplier);
|
||||
} else {
|
||||
$usage->setParam('collections.{scope}.count.total', 1 * $multiplier);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
|
||||
App::init()
|
||||
->groups(['api'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('mails')
|
||||
->inject('usage')
|
||||
->inject('deletes')
|
||||
->inject('database')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Mail $mails, Stats $usage, Delete $deletes, EventDatabase $database, Database $dbForProject, string $mode) use ($databaseListener) {
|
||||
|
||||
foreach ($abuseKeyLabel as $abuseKey) {
|
||||
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
|
||||
$timeLimit
|
||||
$route = $utopia->match($request);
|
||||
|
||||
if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
|
||||
throw new Exception(Exception::PROJECT_UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Abuse Check
|
||||
*/
|
||||
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
|
||||
$timeLimitArray = [];
|
||||
|
||||
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
|
||||
|
||||
foreach ($abuseKeyLabel as $abuseKey) {
|
||||
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
|
||||
$timeLimit
|
||||
->setParam('{userId}', $user->getId())
|
||||
->setParam('{userAgent}', $request->getUserAgent(''))
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getHostname().$route->getPath());
|
||||
$timeLimitArray[] = $timeLimit;
|
||||
}
|
||||
->setParam('{url}', $request->getHostname() . $route->getPath())
|
||||
->setParam('{method}', $request->getMethod());
|
||||
$timeLimitArray[] = $timeLimit;
|
||||
}
|
||||
|
||||
$closestLimit = null;
|
||||
$closestLimit = null;
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
foreach ($timeLimitArray as $timeLimit) {
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
if(!empty($value)) {
|
||||
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
foreach ($timeLimitArray as $timeLimit) {
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
if (!empty($value)) {
|
||||
$timeLimit->setParam('{param-' . $key . '}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
}
|
||||
}
|
||||
|
||||
$abuse = new Abuse($timeLimit);
|
||||
$remaining = $timeLimit->remaining();
|
||||
$limit = $timeLimit->limit();
|
||||
$time = (new \DateTime($timeLimit->time()))->getTimestamp() + $route->getLabel('abuse-time', 3600);
|
||||
|
||||
if ($limit && ($remaining < $closestLimit || is_null($closestLimit))) {
|
||||
$closestLimit = $remaining;
|
||||
$response
|
||||
->addHeader('X-RateLimit-Limit', $limit)
|
||||
->addHeader('X-RateLimit-Remaining', $remaining)
|
||||
->addHeader('X-RateLimit-Reset', $time)
|
||||
;
|
||||
}
|
||||
|
||||
$enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
|
||||
|
||||
if (
|
||||
$enabled // Abuse is enabled
|
||||
&& !$isAppUser // User is not API key
|
||||
&& !$isPrivilegedUser // User is not an admin
|
||||
&& $abuse->check() // Route is rate-limited
|
||||
) {
|
||||
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
if ($timeLimit->limit() && ($timeLimit->remaining() < $closestLimit || is_null($closestLimit))) {
|
||||
$closestLimit = $timeLimit->remaining();
|
||||
$response
|
||||
->addHeader('X-RateLimit-Limit', $timeLimit->limit())
|
||||
->addHeader('X-RateLimit-Remaining', $timeLimit->remaining())
|
||||
->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600))
|
||||
;
|
||||
}
|
||||
|
||||
if ((App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled' // Route is rate-limited
|
||||
&& $abuse->check()) // Abuse is not disabled
|
||||
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||
{
|
||||
throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Background Jobs
|
||||
*/
|
||||
$events
|
||||
->setEvent($route->getLabel('event', ''))
|
||||
->setProject($project)
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
$mails
|
||||
->setProject($project)
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
$audits
|
||||
->setMode($mode)
|
||||
->setUserAgent($request->getUserAgent(''))
|
||||
->setIP($request->getIP())
|
||||
->setEvent($route->getLabel('event', ''))
|
||||
->setProject($project)
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
$usage
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('httpRequest', 1)
|
||||
->setParam('httpUrl', $request->getHostname().$request->getURI())
|
||||
->setParam('httpMethod', $request->getMethod())
|
||||
->setParam('httpPath', $route->getPath())
|
||||
->setParam('networkRequestSize', 0)
|
||||
->setParam('networkResponseSize', 0)
|
||||
->setParam('storage', 0)
|
||||
;
|
||||
|
||||
$deletes->setProject($project);
|
||||
$database->setProject($project);
|
||||
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'mails', 'usage', 'deletes', 'database', 'dbForProject', 'mode'], 'api');
|
||||
|
||||
App::init(function ($utopia, $request, $project) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
switch ($route->getLabel('auth.type', '')) {
|
||||
case 'emailPassword':
|
||||
if(($auths['emailPassword'] ?? true) === false) {
|
||||
throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'magic-url':
|
||||
if($project->getAttribute('usersAuthMagicURL', true) === false) {
|
||||
throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anonymous':
|
||||
if(($auths['anonymous'] ?? true) === false) {
|
||||
throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invites':
|
||||
if(($auths['invites'] ?? true) === false) {
|
||||
throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'jwt':
|
||||
if(($auths['JWT'] ?? true) === false) {
|
||||
throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
}, ['utopia', 'request', 'project'], 'auth');
|
||||
|
||||
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $database, $mode, $dbForProject) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var bool $mode */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
|
||||
if (!empty($events->getEvent())) {
|
||||
if (empty($events->getPayload())) {
|
||||
$events->setPayload($response->getPayload());
|
||||
}
|
||||
/**
|
||||
* Trigger functions.
|
||||
*/
|
||||
/*
|
||||
* Background Jobs
|
||||
*/
|
||||
$events
|
||||
->setClass(Event::FUNCTIONS_CLASS_NAME)
|
||||
->setQueue(Event::FUNCTIONS_QUEUE_NAME)
|
||||
->trigger();
|
||||
->setEvent($route->getLabel('event', ''))
|
||||
->setProject($project)
|
||||
->setUser($user);
|
||||
|
||||
/**
|
||||
* Trigger webhooks.
|
||||
*/
|
||||
$events
|
||||
->setClass(Event::WEBHOOK_CLASS_NAME)
|
||||
->setQueue(Event::WEBHOOK_QUEUE_NAME)
|
||||
->trigger();
|
||||
$mails
|
||||
->setProject($project)
|
||||
->setUser($user);
|
||||
|
||||
/**
|
||||
* Trigger realtime.
|
||||
*/
|
||||
if ($project->getId() !== 'console') {
|
||||
$allEvents = Event::generateEvents($events->getEvent(), $events->getParams());
|
||||
$payload = new Document($events->getPayload());
|
||||
$context = $events->getContext() ?? false;
|
||||
|
||||
$collection = ($context && $context->getCollection() === 'collections') ? $context : null;
|
||||
$bucket = ($context && $context->getCollection() === 'buckets') ? $context : null;
|
||||
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $payload,
|
||||
project: $project,
|
||||
collection: $collection,
|
||||
bucket: $bucket,
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
projectId: $target['projectId'] ?? $project->getId(),
|
||||
payload: $events->getPayload(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'permissionsChanged' => $target['permissionsChanged'],
|
||||
'userId' => $events->getParam('userId')
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($audits->getResource())) {
|
||||
foreach ($events->getParams() as $key => $value) {
|
||||
$audits->setParam($key, $value);
|
||||
}
|
||||
$audits->trigger();
|
||||
}
|
||||
|
||||
if (!empty($deletes->getType())) {
|
||||
$deletes->trigger();
|
||||
}
|
||||
|
||||
if (!empty($database->getType())) {
|
||||
$database->trigger();
|
||||
}
|
||||
|
||||
$route = $utopia->match($request);
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
|
||||
&& $project->getId()
|
||||
&& $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin
|
||||
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
|
||||
$audits
|
||||
->setMode($mode)
|
||||
->setUserAgent($request->getUserAgent(''))
|
||||
->setIP($request->getIP())
|
||||
->setEvent($route->getLabel('audits.event', ''))
|
||||
->setProject($project)
|
||||
->setUser($user);
|
||||
|
||||
$usage
|
||||
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
|
||||
->setParam('networkResponseSize', $response->getSize())
|
||||
->submit();
|
||||
}
|
||||
->setParam('projectInternalId', $project->getInternalId())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('project.{scope}.network.requests', 1)
|
||||
->setParam('httpMethod', $request->getMethod())
|
||||
->setParam('project.{scope}.network.inbound', 0)
|
||||
->setParam('project.{scope}.network.outbound', 0);
|
||||
|
||||
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject'], 'api');
|
||||
$deletes->setProject($project);
|
||||
$database->setProject($project);
|
||||
|
||||
$dbForProject->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, Document $document) => $databaseListener($event, $document, $usage));
|
||||
|
||||
$dbForProject->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, Document $document) => $databaseListener($event, $document, $usage));
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
|
||||
if ($useCache) {
|
||||
$key = md5($request->getURI() . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
);
|
||||
$timestamp = 60 * 60 * 24 * 30;
|
||||
$data = $cache->load($key, $timestamp);
|
||||
|
||||
if (!empty($data)) {
|
||||
$data = json_decode($data, true);
|
||||
$parts = explode('/', $data['resourceType']);
|
||||
$type = $parts[0] ?? null;
|
||||
|
||||
if ($type === 'bucket') {
|
||||
$bucketId = $parts[1] ?? null;
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
if (!$fileSecurity && !$valid) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$parts = explode('/', $data['resource']);
|
||||
$fileId = $parts[1] ?? null;
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
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($data['contentType'])
|
||||
->send(base64_decode($data['payload']))
|
||||
;
|
||||
|
||||
$route->setIsActive(false);
|
||||
} else {
|
||||
$response->addHeader('X-Appwrite-Cache', 'miss');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
->groups(['auth'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('project')
|
||||
->action(function (App $utopia, Request $request, Document $project) {
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
switch ($route->getLabel('auth.type', '')) {
|
||||
case 'emailPassword':
|
||||
if (($auths['emailPassword'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'magic-url':
|
||||
if ($project->getAttribute('usersAuthMagicURL', true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Magic URL authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anonymous':
|
||||
if (($auths['anonymous'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Anonymous authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invites':
|
||||
if (($auths['invites'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Invites authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'jwt':
|
||||
if (($auths['JWT'] ?? true) === false) {
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'JWT authentication is disabled for this project');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
App::shutdown()
|
||||
->groups(['api'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('deletes')
|
||||
->inject('database')
|
||||
->inject('mode')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForFunctions')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, string $mode, Database $dbForProject, Func $queueForFunctions) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
if (!empty($events->getEvent())) {
|
||||
if (empty($events->getPayload())) {
|
||||
$events->setPayload($responsePayload);
|
||||
}
|
||||
/**
|
||||
* Trigger functions.
|
||||
*/
|
||||
$queueForFunctions
|
||||
->from($events)
|
||||
->trigger();
|
||||
|
||||
/**
|
||||
* Trigger webhooks.
|
||||
*/
|
||||
$events
|
||||
->setClass(Event::WEBHOOK_CLASS_NAME)
|
||||
->setQueue(Event::WEBHOOK_QUEUE_NAME)
|
||||
->trigger();
|
||||
|
||||
/**
|
||||
* Trigger realtime.
|
||||
*/
|
||||
if ($project->getId() !== 'console') {
|
||||
$allEvents = Event::generateEvents($events->getEvent(), $events->getParams());
|
||||
$payload = new Document($events->getPayload());
|
||||
|
||||
$db = $events->getContext('database');
|
||||
$collection = $events->getContext('collection');
|
||||
$bucket = $events->getContext('bucket');
|
||||
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $payload,
|
||||
project: $project,
|
||||
database: $db,
|
||||
collection: $collection,
|
||||
bucket: $bucket,
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
projectId: $target['projectId'] ?? $project->getId(),
|
||||
payload: $events->getPayload(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'permissionsChanged' => $target['permissionsChanged'],
|
||||
'userId' => $events->getParam('userId')
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$route = $utopia->match($request);
|
||||
$requestParams = $route->getParamsValues();
|
||||
$user = $audits->getUser();
|
||||
|
||||
/**
|
||||
* Audit labels
|
||||
*/
|
||||
$pattern = $route->getLabel('audits.resource', null);
|
||||
if (!empty($pattern)) {
|
||||
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
if (!empty($resource) && $resource !== $pattern) {
|
||||
$audits->setResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
$pattern = $route->getLabel('audits.userId', null);
|
||||
if (!empty($pattern)) {
|
||||
$userId = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
$audits->setUser($user);
|
||||
}
|
||||
|
||||
if (!empty($audits->getResource()) && !empty($audits->getUser()->getId())) {
|
||||
/**
|
||||
* audits.payload is switched to default true
|
||||
* in order to auto audit payload for all endpoints
|
||||
*/
|
||||
$pattern = $route->getLabel('audits.payload', true);
|
||||
if (!empty($pattern)) {
|
||||
$audits->setPayload($responsePayload);
|
||||
}
|
||||
|
||||
foreach ($events->getParams() as $key => $value) {
|
||||
$audits->setParam($key, $value);
|
||||
}
|
||||
$audits->trigger();
|
||||
}
|
||||
|
||||
if (!empty($deletes->getType())) {
|
||||
$deletes->trigger();
|
||||
}
|
||||
|
||||
if (!empty($database->getType())) {
|
||||
$database->trigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache label
|
||||
*/
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
if ($useCache) {
|
||||
$resource = $resourceType = null;
|
||||
$data = $response->getPayload();
|
||||
|
||||
if (!empty($data['payload'])) {
|
||||
$pattern = $route->getLabel('cache.resource', null);
|
||||
if (!empty($pattern)) {
|
||||
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$pattern = $route->getLabel('cache.resourceType', null);
|
||||
if (!empty($pattern)) {
|
||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$key = md5($request->getURI() . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
|
||||
$data = json_encode([
|
||||
'resourceType' => $resourceType,
|
||||
'resource' => $resource,
|
||||
'contentType' => $response->getContentType(),
|
||||
'payload' => base64_encode($data['payload']),
|
||||
]) ;
|
||||
|
||||
$signature = md5($data);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||
$now = DateTime::now();
|
||||
if ($cacheLog->isEmpty()) {
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
|
||||
'$id' => $key,
|
||||
'resource' => $resource,
|
||||
'accessedAt' => $now,
|
||||
'signature' => $signature,
|
||||
])));
|
||||
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
|
||||
$cacheLog->setAttribute('accessedAt', $now);
|
||||
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
|
||||
}
|
||||
|
||||
if ($signature !== $cacheLog->getAttribute('signature')) {
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
);
|
||||
$cache->save($key, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
|
||||
&& $project->getId()
|
||||
&& !empty($route->getLabel('sdk.namespace', null))
|
||||
) { // Don't calculate console usage on admin mode
|
||||
$metric = $route->getLabel('usage.metric', '');
|
||||
$usageParams = $route->getLabel('usage.params', []);
|
||||
|
||||
if (!empty($metric)) {
|
||||
$usage->setParam($metric, 1);
|
||||
foreach ($usageParams as $param) {
|
||||
$param = $parseLabel($param, $responsePayload, $requestParams, $user);
|
||||
$parts = explode(':', $param);
|
||||
if (count($parts) != 2) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Usage params not properly set');
|
||||
}
|
||||
$usage->setParam($parts[0], $parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$fileSize = 0;
|
||||
$file = $request->getFiles('file');
|
||||
if (!empty($file)) {
|
||||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('project.{scope}.network.inbound', $request->getSize() + $fileSize)
|
||||
->setParam('project.{scope}.network.outbound', $response->getSize())
|
||||
->submit();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $layout) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
/* AJAX check */
|
||||
if (!empty($request->getQuery('version', ''))) {
|
||||
$layout->setPath(__DIR__ . '/../../views/layouts/empty.phtml');
|
||||
}
|
||||
|
||||
$port = $request->getPort();
|
||||
$protocol = $request->getProtocol();
|
||||
$domain = $request->getHostname();
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
->setParam('protocol', $protocol)
|
||||
->setParam('domain', $domain)
|
||||
->setParam('endpoint', $protocol . '://' . $domain . ($port != 80 && $port != 443 ? ':' . $port : ''))
|
||||
->setParam('home', App::getEnv('_APP_HOME'))
|
||||
->setParam('setup', App::getEnv('_APP_SETUP'))
|
||||
->setParam('class', 'unknown')
|
||||
->setParam('icon', '/images/favicon.png')
|
||||
->setParam('roles', [
|
||||
['type' => 'owner', 'label' => 'Owner'],
|
||||
['type' => 'developer', 'label' => 'Developer'],
|
||||
['type' => 'admin', 'label' => 'Admin'],
|
||||
])
|
||||
->setParam('runtimes', Config::getParam('runtimes'))
|
||||
->setParam('mode', App::getMode())
|
||||
;
|
||||
|
||||
$time = (60 * 60 * 24 * 45); // 45 days cache
|
||||
|
||||
$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('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes
|
||||
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI()))
|
||||
->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode
|
||||
;
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$route->label('error', __DIR__.'/../../views/general/error.phtml');
|
||||
|
||||
$scope = $route->getLabel('scope', '');
|
||||
|
||||
$layout
|
||||
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
|
||||
->setParam('isDev', App::isDevelopment())
|
||||
->setParam('class', $scope)
|
||||
;
|
||||
}, ['utopia', 'request', 'response', 'layout'], 'web');
|
||||
+48
-484
@@ -1,497 +1,61 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\View;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$layout
|
||||
->setParam('description', 'Appwrite Console allows you to easily manage, monitor, and control your entire backend API and tools.')
|
||||
->setParam('analytics', 'UA-26264668-5')
|
||||
;
|
||||
}, ['layout'], 'console');
|
||||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/console/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('home', App::getEnv('_APP_HOME', ''))
|
||||
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('header', [$header])
|
||||
->setParam('footer', [$footer])
|
||||
;
|
||||
|
||||
$response->html($layout->render());
|
||||
}, ['response', 'layout'], 'console');
|
||||
|
||||
App::get('/error/:code')
|
||||
->groups(['web', 'console'])
|
||||
App::get('/console/*')
|
||||
->alias('/')
|
||||
->alias('auth/*')
|
||||
->alias('/invite')
|
||||
->alias('/login')
|
||||
->alias('/recover')
|
||||
->alias('/register/*')
|
||||
->groups(['web'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Error')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', ''))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Console')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/account')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/account/index.phtml');
|
||||
|
||||
$cc = new View(__DIR__.'/../../views/console/forms/credit-card.phtml');
|
||||
|
||||
$page
|
||||
->setParam('cc', $cc)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Account - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/notifications')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/v1/console/notifications/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Notifications')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/home')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
|
||||
$page
|
||||
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Console')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/settings')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/settings/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('services', array_filter(Config::getParam('services'), function($element) {return $element['optional'];}))
|
||||
->setParam('customDomainsEnabled', ($target->isKnown() && !$target->isTest()))
|
||||
->setParam('customDomainsTarget', $target->get())
|
||||
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Settings')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/webhooks')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/index.phtml');
|
||||
|
||||
$page->setParam('events', Config::getParam('events', []));
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/webhooks/webhook')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Webhook unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($id, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/webhook.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
->setParam('new', false)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/webhooks/webhook/new')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/webhook.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
->setParam('new', true)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/keys')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$scopes = array_keys(Config::getParam('scopes'));
|
||||
$page = new View(__DIR__.'/../../views/console/keys/index.phtml');
|
||||
|
||||
$page->setParam('scopes', $scopes);
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - API Keys')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/database')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/database/collection')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Collection unique ID.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (Request $request, Response $response) {
|
||||
$fallback = file_get_contents(__DIR__ . '/../../../console/index.html');
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
// Card SSR
|
||||
if (\str_starts_with($request->getURI(), '/card')) {
|
||||
$urlCunks = \explode('/', $request->getURI());
|
||||
$userId = $urlCunks[\count($urlCunks) - 1] ?? '';
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listCollectionLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
$domain = $request->getProtocol() . '://' . $request->getHostname();
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
|
||||
|
||||
$page->setParam('logs', $logs);
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Collection')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/console/database/document')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listDocumentLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'document-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', false)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('logs', $logs)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/database/document/new')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', true)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('logs', new View())
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/storage')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/console/storage/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/storage/bucket')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Bucket unique ID.')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/storage/bucket.phtml');
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage Buckets')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/console/users')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('auth', Config::getParam('auth'))
|
||||
->setParam('providers', Config::getParam('providers'))
|
||||
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Users')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/users/user')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/user.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - User')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/users/teams/team')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/team.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Team')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/functions')
|
||||
->groups(['web', 'console'])
|
||||
->desc('Platform console project functions')
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
$page = new View(__DIR__.'/../../views/console/functions/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('runtimes', Config::getParam('runtimes'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Functions')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/functions/function')
|
||||
->groups(['web', 'console'])
|
||||
->desc('Platform console project function')
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
$page = new View(__DIR__.'/../../views/console/functions/function.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
->setParam('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))
|
||||
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Function')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/version')
|
||||
->groups(['web', 'console'])
|
||||
->desc('Check for new version')
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
try {
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
|
||||
|
||||
if ($version && isset($version['version'])) {
|
||||
return $response->json(['version' => $version['version']]);
|
||||
if (!empty($userId)) {
|
||||
$ogImageUrl = $domain . '/v1/cards/cloud-og?userId=' . $userId;
|
||||
} else {
|
||||
throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
$ogImageUrl = $domain . '/v1/cards/cloud-og?mock=normal';
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
|
||||
$ogTags = [
|
||||
'<title>Appwrite Cloud Card</title>',
|
||||
'<meta name="description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
|
||||
'<meta property="og:url" content="' . $domain . $request->getURI() . '">',
|
||||
'<meta name="og:image:type" content="image/png">',
|
||||
'<meta name="og:image:width" content="1008">',
|
||||
'<meta name="og:image:height" content="1008">',
|
||||
'<meta property="og:type" content="website">',
|
||||
'<meta property="og:title" content="Appwrite Cloud Card">',
|
||||
'<meta property="og:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
|
||||
'<meta property="og:image" content="' . $ogImageUrl . '">',
|
||||
'<meta name="twitter:card" content="summary_large_image">',
|
||||
'<meta property="twitter:domain" content="' . $request->getHostname() . '">',
|
||||
'<meta property="twitter:url" content="' . $domain . $request->getURI() . '">',
|
||||
'<meta name="twitter:title" content="Appwrite Cloud Card">',
|
||||
'<meta name="twitter:image:type" content="image/png">',
|
||||
'<meta name="twitter:image:width" content="1008">',
|
||||
'<meta name="twitter:image:height" content="1008">',
|
||||
'<meta name="twitter:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
|
||||
'<meta name="twitter:image" content="' . $ogImageUrl . '">',
|
||||
];
|
||||
|
||||
$fallback = \str_replace('<!-- {{CLOUD_OG}} -->', \implode('', $ogTags), $fallback);
|
||||
}
|
||||
});
|
||||
|
||||
$response->html($fallback);
|
||||
});
|
||||
|
||||
@@ -1,267 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\View;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
->setParam('description', '')
|
||||
->setParam('class', 'home')
|
||||
->setParam('platforms', Config::getParam('platforms'))
|
||||
->setParam('header', [$header])
|
||||
->setParam('footer', [$footer])
|
||||
;
|
||||
}, ['layout'], 'home');
|
||||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$response->html($layout->render());
|
||||
}, ['response', 'layout'], 'home');
|
||||
|
||||
App::get('/')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->inject('project')
|
||||
->action(function ($response, $dbForConsole, $project) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
;
|
||||
|
||||
if ('console' === $project->getId() || $project->isEmpty()) {
|
||||
$whitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled');
|
||||
|
||||
if($whitelistRoot !== 'disabled') {
|
||||
$count = $dbForConsole->count('users', [], 1);
|
||||
|
||||
if($count !== 0) {
|
||||
return $response->redirect('/auth/signin');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->redirect('/auth/signup');
|
||||
});
|
||||
|
||||
App::get('/auth/signin')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
|
||||
|
||||
$page
|
||||
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign In - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/signup')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
|
||||
|
||||
$page
|
||||
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign Up - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/recovery')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
|
||||
|
||||
$page
|
||||
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Recovery - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/confirm')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/confirm.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Account Confirmation - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/join')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/join.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Invitation - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/recovery/reset')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery/reset.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Reset - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/auth/oauth2/success')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
->setParam('body', $page)
|
||||
->setParam('header', [])
|
||||
->setParam('footer', [])
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/auth/magic-url')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/magicURL.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
->setParam('body', $page)
|
||||
->setParam('header', [])
|
||||
->setParam('footer', [])
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/auth/oauth2/failure')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
->setParam('body', $page)
|
||||
->setParam('header', [])
|
||||
->setParam('footer', [])
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/error/:code')
|
||||
->groups(['web', 'home'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Error'.' - '.APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/versions')
|
||||
->desc('Get Version')
|
||||
->groups(['web', 'home'])
|
||||
->label('scope', 'public')
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
||||
->action(function (Response $response) {
|
||||
$platforms = Config::getParam('platforms');
|
||||
|
||||
$versions = [
|
||||
'server' => APP_VERSION_STABLE,
|
||||
];
|
||||
|
||||
foreach($platforms as $platform) {
|
||||
foreach ($platforms as $platform) {
|
||||
$languages = $platform['languages'] ?? [];
|
||||
|
||||
foreach ($languages as $key => $language) {
|
||||
if(isset($language['dev']) && $language['dev']) {
|
||||
if (isset($language['dev']) && $language['dev']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isset($language['enabled']) && !$language['enabled']) {
|
||||
if (isset($language['enabled']) && !$language['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,724 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
use Swoole\ConnectionPool;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
use Swoole\Http\Server;
|
||||
use Swoole\Process;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\Timer;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Orchestration\Adapter\DockerCLI;
|
||||
use Utopia\Orchestration\Orchestration;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\Swoole\Response;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
|
||||
Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
||||
|
||||
/** Constants */
|
||||
const MAINTENANCE_INTERVAL = 3600; // 3600 seconds = 1 hour
|
||||
|
||||
/**
|
||||
* Create a Swoole table to store runtime information
|
||||
*/
|
||||
$activeRuntimes = new Swoole\Table(1024);
|
||||
$activeRuntimes->column('id', Swoole\Table::TYPE_STRING, 256);
|
||||
$activeRuntimes->column('created', Swoole\Table::TYPE_INT, 8);
|
||||
$activeRuntimes->column('updated', Swoole\Table::TYPE_INT, 8);
|
||||
$activeRuntimes->column('name', Swoole\Table::TYPE_STRING, 128);
|
||||
$activeRuntimes->column('status', Swoole\Table::TYPE_STRING, 128);
|
||||
$activeRuntimes->column('key', Swoole\Table::TYPE_STRING, 256);
|
||||
$activeRuntimes->create();
|
||||
|
||||
/**
|
||||
* Create orchestration pool
|
||||
*/
|
||||
$orchestrationPool = new ConnectionPool(function () {
|
||||
$dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', null);
|
||||
$dockerPass = App::getEnv('DOCKERHUB_PULL_PASSWORD', null);
|
||||
$orchestration = new Orchestration(new DockerCLI($dockerUser, $dockerPass));
|
||||
return $orchestration;
|
||||
}, 10);
|
||||
|
||||
|
||||
/**
|
||||
* Create logger instance
|
||||
*/
|
||||
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
$logger = null;
|
||||
|
||||
if(!empty($providerName) && !empty($providerConfig) && Logger::hasProvider($providerName)) {
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
|
||||
$adapter = new $classname($providerConfig);
|
||||
$logger = new Logger($adapter);
|
||||
}
|
||||
|
||||
function logError(Throwable $error, string $action, Utopia\Route $route = null)
|
||||
{
|
||||
global $logger;
|
||||
|
||||
if ($logger) {
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace("executor");
|
||||
$log->setServer(\gethostname());
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
||||
if ($route) {
|
||||
$log->addTag('method', $route->getMethod());
|
||||
$log->addTag('url', $route->getPath());
|
||||
}
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Executor log pushed with status code: ' . $responseCode);
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
Console::error('[Error] Message: ' . $error->getMessage());
|
||||
Console::error('[Error] File: ' . $error->getFile());
|
||||
Console::error('[Error] Line: ' . $error->getLine());
|
||||
};
|
||||
|
||||
function getStorageDevice($root): Device {
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
}
|
||||
}
|
||||
|
||||
App::post('/v1/runtimes')
|
||||
->desc("Create a new runtime server")
|
||||
->param('runtimeId', '', new Text(64), 'Unique runtime ID.')
|
||||
->param('source', '', new Text(0), 'Path to source files.')
|
||||
->param('destination', '', new Text(0), 'Destination folder to store build files into.', true)
|
||||
->param('vars', [], new Assoc(), 'Environment Variables required for the build.')
|
||||
->param('commands', [], new ArrayList(new Text(1024), 100), 'Commands required to build the container. Maximum of 100 commands are allowed, each 1024 characters long.')
|
||||
->param('runtime', '', new Text(128), 'Runtime for the cloud function.')
|
||||
->param('baseImage', '', new Text(128), 'Base image name of the runtime.')
|
||||
->param('entrypoint', '', new Text(256), 'Entrypoint of the code file.', true)
|
||||
->param('remove', false, new Boolean(), 'Remove a runtime after execution.')
|
||||
->param('workdir', '', new Text(256), 'Working directory.', true)
|
||||
->inject('orchestrationPool')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(function (string $runtimeId, string $source, string $destination, array $vars, array $commands, string $runtime, string $baseImage, string $entrypoint, bool $remove, string $workdir, $orchestrationPool, $activeRuntimes, Response $response) {
|
||||
if ($activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime already exists.', 409);
|
||||
}
|
||||
|
||||
$container = [];
|
||||
$containerId = '';
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$startTime = \time();
|
||||
$endTime = 0;
|
||||
$orchestration = $orchestrationPool->get();
|
||||
|
||||
try {
|
||||
Console::info('Building container : ' . $runtimeId);
|
||||
|
||||
/**
|
||||
* Temporary file paths in the executor
|
||||
*/
|
||||
$tmpSource = "/tmp/$runtimeId/src/code.tar.gz";
|
||||
$tmpBuild = "/tmp/$runtimeId/builds/code.tar.gz";
|
||||
|
||||
/**
|
||||
* Copy code files from source to a temporary location on the executor
|
||||
*/
|
||||
$sourceDevice = getStorageDevice("/");
|
||||
$localDevice = new Local();
|
||||
$buffer = $sourceDevice->read($source);
|
||||
if(!$localDevice->write($tmpSource, $buffer)) {
|
||||
throw new Exception('Failed to copy source code to temporary directory', 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the mount folder
|
||||
*/
|
||||
if (!\file_exists(\dirname($tmpBuild))) {
|
||||
if (!@\mkdir(\dirname($tmpBuild), 0755, true)) {
|
||||
throw new Exception("Failed to create temporary directory", 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create container
|
||||
*/
|
||||
$secret = \bin2hex(\random_bytes(16));
|
||||
$vars = \array_merge($vars, [
|
||||
'INTERNAL_RUNTIME_KEY' => $secret,
|
||||
'INTERNAL_RUNTIME_ENTRYPOINT' => $entrypoint,
|
||||
]);
|
||||
$vars = array_map(fn ($v) => strval($v), $vars);
|
||||
$orchestration
|
||||
->setCpus((int) App::getEnv('_APP_FUNCTIONS_CPUS', 0))
|
||||
->setMemory((int) App::getEnv('_APP_FUNCTIONS_MEMORY', 0))
|
||||
->setSwap((int) App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 0));
|
||||
|
||||
/** Keep the container alive if we have commands to be executed */
|
||||
$entrypoint = !empty($commands) ? [
|
||||
'tail',
|
||||
'-f',
|
||||
'/dev/null'
|
||||
] : [];
|
||||
|
||||
$containerId = $orchestration->run(
|
||||
image: $baseImage,
|
||||
name: $runtimeId,
|
||||
hostname: $runtimeId,
|
||||
vars: $vars,
|
||||
command: $entrypoint,
|
||||
labels: [
|
||||
'openruntimes-id' => $runtimeId,
|
||||
'openruntimes-type' => 'runtime',
|
||||
'openruntimes-created' => strval($startTime),
|
||||
'openruntimes-runtime' => $runtime,
|
||||
],
|
||||
workdir: $workdir,
|
||||
volumes: [
|
||||
\dirname($tmpSource). ':/tmp:rw',
|
||||
\dirname($tmpBuild). ':/usr/code:rw'
|
||||
]
|
||||
);
|
||||
|
||||
if (empty($containerId)) {
|
||||
throw new Exception('Failed to create build container', 500);
|
||||
}
|
||||
|
||||
$orchestration->networkConnect($runtimeId, App::getEnv('OPEN_RUNTIMES_NETWORK', 'appwrite_runtimes'));
|
||||
|
||||
/**
|
||||
* Execute any commands if they were provided
|
||||
*/
|
||||
if (!empty($commands)) {
|
||||
$status = $orchestration->execute(
|
||||
name: $runtimeId,
|
||||
command: $commands,
|
||||
stdout: $stdout,
|
||||
stderr: $stderr,
|
||||
timeout: App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900)
|
||||
);
|
||||
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to build dependenices ' . $stderr, 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move built code to expected build directory
|
||||
*/
|
||||
if (!empty($destination)) {
|
||||
// Check if the build was successful by checking if file exists
|
||||
if (!\file_exists($tmpBuild)) {
|
||||
throw new Exception('Something went wrong during the build process', 500);
|
||||
}
|
||||
|
||||
$destinationDevice = getStorageDevice($destination);
|
||||
$outputPath = $destinationDevice->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
|
||||
$buffer = $localDevice->read($tmpBuild);
|
||||
if(!$destinationDevice->write($outputPath, $buffer, $localDevice->getFileMimeType($tmpBuild))) {
|
||||
throw new Exception('Failed to move built code to storage', 500);
|
||||
};
|
||||
|
||||
$container['outputPath'] = $outputPath;
|
||||
}
|
||||
|
||||
if (empty($stdout)) {
|
||||
$stdout = 'Build Successful!';
|
||||
}
|
||||
|
||||
$endTime = \time();
|
||||
$container = array_merge($container, [
|
||||
'status' => 'ready',
|
||||
'response' => \mb_strcut($stdout, 0, 1000000), // Limit to 1MB
|
||||
'stderr' => \mb_strcut($stderr, 0, 1000000), // Limit to 1MB
|
||||
'startTime' => $startTime,
|
||||
'endTime' => $endTime,
|
||||
'duration' => $endTime - $startTime,
|
||||
]);
|
||||
|
||||
if (!$remove) {
|
||||
$activeRuntimes->set($runtimeId, [
|
||||
'id' => $containerId,
|
||||
'name' => $runtimeId,
|
||||
'created' => $startTime,
|
||||
'updated' => $endTime,
|
||||
'status' => 'Up ' . \round($endTime - $startTime, 2) . 's',
|
||||
'key' => $secret,
|
||||
]);
|
||||
}
|
||||
|
||||
Console::success('Build Stage completed in ' . ($endTime - $startTime) . ' seconds');
|
||||
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Build failed: ' . $th->getMessage() . $stdout);
|
||||
throw new Exception($th->getMessage() . $stdout, 500);
|
||||
} finally {
|
||||
// Container cleanup
|
||||
if($remove) {
|
||||
if (!empty($containerId)) {
|
||||
// If container properly created
|
||||
$orchestration->remove($containerId, true);
|
||||
} else {
|
||||
// If whole creation failed, but container might have been initialized
|
||||
try {
|
||||
// Try to remove with contaier name instead of ID
|
||||
$orchestration->remove($runtimeId, true);
|
||||
} catch (Throwable $th) {
|
||||
// If fails, means initialization also failed.
|
||||
// Contianer is not there, no need to remove
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release orchestration back to pool, we are done with it
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json($container);
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/runtimes')
|
||||
->desc("List currently active runtimes")
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(function ($activeRuntimes, Response $response) {
|
||||
$runtimes = [];
|
||||
|
||||
foreach($activeRuntimes as $runtime) {
|
||||
$runtimes[] = $runtime;
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_OK)
|
||||
->json($runtimes);
|
||||
});
|
||||
|
||||
App::get('/v1/runtimes/:runtimeId')
|
||||
->desc("Get a runtime by its ID")
|
||||
->param('runtimeId', '', new Text(64), 'Runtime unique ID.')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(function ($runtimeId, $activeRuntimes, Response $response) {
|
||||
|
||||
if(!$activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime not found', 404);
|
||||
}
|
||||
|
||||
$runtime = $activeRuntimes->get($runtimeId);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_OK)
|
||||
->json($runtime);
|
||||
});
|
||||
|
||||
App::delete('/v1/runtimes/:runtimeId')
|
||||
->desc('Delete a runtime')
|
||||
->param('runtimeId', '', new Text(64), 'Runtime unique ID.', false)
|
||||
->inject('orchestrationPool')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(function (string $runtimeId, $orchestrationPool, $activeRuntimes, Response $response) {
|
||||
|
||||
if(!$activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime not found', 404);
|
||||
}
|
||||
|
||||
Console::info('Deleting runtime: ' . $runtimeId);
|
||||
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orchestration->remove($runtimeId, true);
|
||||
$activeRuntimes->del($runtimeId);
|
||||
Console::success('Removed runtime container: ' . $runtimeId);
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
|
||||
// Remove all the build containers with that same ID
|
||||
// TODO:: Delete build containers
|
||||
// foreach ($buildIds as $buildId) {
|
||||
// try {
|
||||
// Console::info('Deleting build container : ' . $buildId);
|
||||
// $status = $orchestration->remove('build-' . $buildId, true);
|
||||
// } catch (Throwable $th) {
|
||||
// Console::error($th->getMessage());
|
||||
// }
|
||||
// }
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_OK)
|
||||
->send();
|
||||
});
|
||||
|
||||
|
||||
App::post('/v1/execution')
|
||||
->desc('Create an execution')
|
||||
->param('runtimeId', '', new Text(64), 'The runtimeID to execute.')
|
||||
->param('vars', [], new Assoc(), 'Environment variables required for the build.')
|
||||
->param('data', '{}', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
->action(
|
||||
function (string $runtimeId, array $vars, string $data, $timeout, $activeRuntimes, Response $response) {
|
||||
|
||||
if (!$activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime not found. Please create the runtime.', 404);
|
||||
}
|
||||
|
||||
$runtime = $activeRuntimes->get($runtimeId);
|
||||
$secret = $runtime['key'];
|
||||
if (empty($secret)) {
|
||||
throw new Exception('Runtime secret not found. Please re-create the runtime.', 500);
|
||||
}
|
||||
|
||||
Console::info('Executing Runtime: ' . $runtimeId);
|
||||
|
||||
$execution = [];
|
||||
$executionStart = \microtime(true);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$statusCode = 0;
|
||||
$errNo = -1;
|
||||
$executorResponse = '';
|
||||
|
||||
$timeout ??= (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900);
|
||||
|
||||
$ch = \curl_init();
|
||||
$body = \json_encode([
|
||||
'env' => $vars,
|
||||
'payload' => $data,
|
||||
'timeout' => $timeout
|
||||
]);
|
||||
\curl_setopt($ch, CURLOPT_URL, "http://" . $runtimeId . ":3000/");
|
||||
\curl_setopt($ch, CURLOPT_POST, true);
|
||||
\curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . \strlen($body),
|
||||
'x-internal-challenge: ' . $secret,
|
||||
'host: null'
|
||||
]);
|
||||
|
||||
$executorResponse = \curl_exec($ch);
|
||||
|
||||
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
$error = \curl_error($ch);
|
||||
|
||||
$errNo = \curl_errno($ch);
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
switch (true) {
|
||||
/** No Error. */
|
||||
case $errNo === 0:
|
||||
break;
|
||||
/** Runtime not ready for requests yet. 111 is the swoole error code for Connection Refused - see https://openswoole.com/docs/swoole-error-code */
|
||||
case $errNo === 111:
|
||||
throw new Exception('An internal curl error has occurred within the executor! Error Msg: ' . $error, 406);
|
||||
/** Any other CURL error */
|
||||
default:
|
||||
throw new Exception('An internal curl error has occurred within the executor! Error Msg: ' . $error, 500);
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $statusCode >= 500:
|
||||
$stderr = $executorResponse ?? 'Internal Runtime error.';
|
||||
break;
|
||||
case $statusCode >= 100:
|
||||
$stdout = $executorResponse;
|
||||
break;
|
||||
default:
|
||||
$stderr = $executorResponse ?? 'Execution failed.';
|
||||
break;
|
||||
}
|
||||
|
||||
$executionEnd = \microtime(true);
|
||||
$executionTime = ($executionEnd - $executionStart);
|
||||
$functionStatus = ($statusCode >= 500) ? 'failed' : 'completed';
|
||||
|
||||
Console::success('Function executed in ' . $executionTime . ' seconds, status: ' . $functionStatus);
|
||||
|
||||
$execution = [
|
||||
'status' => $functionStatus,
|
||||
'statusCode' => $statusCode,
|
||||
'response' => \mb_strcut($stdout, 0, 1000000), // Limit to 1MB
|
||||
'stderr' => \mb_strcut($stderr, 0, 1000000), // Limit to 1MB
|
||||
'time' => $executionTime,
|
||||
];
|
||||
|
||||
/** Update swoole table */
|
||||
$runtime['updated'] = \time();
|
||||
$activeRuntimes->set($runtimeId, $runtime);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_OK)
|
||||
->json($execution);
|
||||
}
|
||||
);
|
||||
|
||||
App::setMode(App::MODE_TYPE_PRODUCTION); // Define Mode
|
||||
|
||||
$http = new Server("0.0.0.0", 80);
|
||||
|
||||
/** Set Resources */
|
||||
App::setResource('orchestrationPool', fn() => $orchestrationPool);
|
||||
App::setResource('activeRuntimes', fn() => $activeRuntimes);
|
||||
|
||||
/** Set callbacks */
|
||||
App::error(function ($utopia, $error, $request, $response) {
|
||||
$route = $utopia->match($request);
|
||||
logError($error, "httpError", $route);
|
||||
|
||||
switch ($error->getCode()) {
|
||||
case 400: // Error allowed publicly
|
||||
case 401: // Error allowed publicly
|
||||
case 402: // Error allowed publicly
|
||||
case 403: // Error allowed publicly
|
||||
case 404: // Error allowed publicly
|
||||
case 406: // Error allowed publicly
|
||||
case 409: // Error allowed publicly
|
||||
case 412: // Error allowed publicly
|
||||
case 425: // Error allowed publicly
|
||||
case 429: // Error allowed publicly
|
||||
case 501: // Error allowed publicly
|
||||
case 503: // Error allowed publicly
|
||||
$code = $error->getCode();
|
||||
break;
|
||||
default:
|
||||
$code = 500; // All other errors get the generic 500 server error status code
|
||||
}
|
||||
|
||||
$output = [
|
||||
'message' => $error->getMessage(),
|
||||
'code' => $error->getCode(),
|
||||
'file' => $error->getFile(),
|
||||
'line' => $error->getLine(),
|
||||
'trace' => $error->getTrace(),
|
||||
'version' => App::getEnv('_APP_VERSION', 'UNKNOWN')
|
||||
];
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Expires', '0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->setStatusCode($code);
|
||||
|
||||
$response->json($output);
|
||||
}, ['utopia', 'error', 'request', 'response']);
|
||||
|
||||
App::init(function ($request, $response) {
|
||||
$secretKey = $request->getHeader('x-appwrite-executor-key', '');
|
||||
if (empty($secretKey)) {
|
||||
throw new Exception('Missing executor key', 401);
|
||||
}
|
||||
|
||||
if ($secretKey !== App::getEnv('_APP_EXECUTOR_SECRET', '')) {
|
||||
throw new Exception('Missing executor key', 401);
|
||||
}
|
||||
}, ['request', 'response']);
|
||||
|
||||
|
||||
$http->on('start', function ($http) {
|
||||
global $orchestrationPool;
|
||||
global $activeRuntimes;
|
||||
|
||||
/**
|
||||
* Warmup: make sure images are ready to run fast 🚀
|
||||
*/
|
||||
$runtimes = new Runtimes('v1');
|
||||
$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES'));
|
||||
$runtimes = $runtimes->getAll(true, $allowList);
|
||||
foreach ($runtimes as $runtime) {
|
||||
go(function () use ($runtime, $orchestrationPool) {
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
Console::info('Warming up ' . $runtime['name'] . ' ' . $runtime['version'] . ' environment...');
|
||||
$response = $orchestration->pull($runtime['image']);
|
||||
if ($response) {
|
||||
Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
|
||||
} else {
|
||||
Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove residual runtimes
|
||||
*/
|
||||
Console::info('Removing orphan runtimes...');
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orphans = $orchestration->list(['label' => 'openruntimes-type=runtime']);
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
|
||||
foreach ($orphans as $runtime) {
|
||||
go(function () use ($runtime, $orchestrationPool) {
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orchestration->remove($runtime->getName(), true);
|
||||
Console::success("Successfully removed {$runtime->getName()}");
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Orphan runtime deletion failed: ' . $th->getMessage());
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register handlers for shutdown
|
||||
*/
|
||||
@Process::signal(SIGINT, function () use ($http) {
|
||||
$http->shutdown();
|
||||
});
|
||||
|
||||
@Process::signal(SIGQUIT, function () use ($http) {
|
||||
$http->shutdown();
|
||||
});
|
||||
|
||||
@Process::signal(SIGKILL, function () use ($http) {
|
||||
$http->shutdown();
|
||||
});
|
||||
|
||||
@Process::signal(SIGTERM, function () use ($http) {
|
||||
$http->shutdown();
|
||||
});
|
||||
|
||||
/**
|
||||
* Run a maintenance worker every MAINTENANCE_INTERVAL seconds to remove inactive runtimes
|
||||
*/
|
||||
Timer::tick(MAINTENANCE_INTERVAL * 1000, function () use ($orchestrationPool, $activeRuntimes) {
|
||||
Console::warning("Running maintenance task ...");
|
||||
foreach ($activeRuntimes as $runtime) {
|
||||
$inactiveThreshold = \time() - App::getEnv('_APP_FUNCTIONS_INACTIVE_THRESHOLD', 60);
|
||||
if ($runtime['updated'] < $inactiveThreshold) {
|
||||
go(function () use ($runtime, $orchestrationPool, $activeRuntimes) {
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orchestration->remove($runtime['name'], true);
|
||||
$activeRuntimes->del($runtime['name']);
|
||||
Console::success("Successfully removed {$runtime['name']}");
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Inactive Runtime deletion failed: ' . $th->getMessage());
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
$http->on('beforeShutdown', function() {
|
||||
global $orchestrationPool;
|
||||
Console::info('Cleaning up containers before shutdown...');
|
||||
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$functionsToRemove = $orchestration->list(['label' => 'openruntimes-type=runtime']);
|
||||
$orchestrationPool->put($orchestration);
|
||||
|
||||
foreach ($functionsToRemove as $container) {
|
||||
go(function () use ($orchestrationPool, $container) {
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orchestration->remove($container->getId(), true);
|
||||
Console::info('Removed container ' . $container->getName());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to remove container: ' . $container->getName());
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) {
|
||||
$request = new Request($swooleRequest);
|
||||
$response = new Response($swooleResponse);
|
||||
$app = new App('UTC');
|
||||
|
||||
try {
|
||||
$app->run($request, $response);
|
||||
} catch (\Throwable $th) {
|
||||
logError($th, "serverError");
|
||||
$swooleResponse->setStatusCode(500);
|
||||
$output = [
|
||||
'message' => 'Error: '. $th->getMessage(),
|
||||
'code' => 500,
|
||||
'file' => $th->getFile(),
|
||||
'line' => $th->getLine(),
|
||||
'trace' => $th->getTrace()
|
||||
];
|
||||
$swooleResponse->end(\json_encode($output));
|
||||
}
|
||||
});
|
||||
|
||||
$http->start();
|
||||
+82
-93
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Process;
|
||||
@@ -10,6 +10,9 @@ use Swoole\Http\Response as SwooleResponse;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Permission;
|
||||
use Utopia\Database\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
@@ -19,12 +22,12 @@ use Utopia\Swoole\Files;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\User;
|
||||
use Utopia\Pools\Group;
|
||||
|
||||
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
|
||||
|
||||
$payloadSize = 6 * (1024 * 1024); // 6MB
|
||||
$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$http
|
||||
->set([
|
||||
'worker_num' => $workerNumber,
|
||||
@@ -35,29 +38,31 @@ $http
|
||||
'http_compression_level' => 6,
|
||||
'package_max_length' => $payloadSize,
|
||||
'buffer_output_size' => $payloadSize,
|
||||
])
|
||||
;
|
||||
]);
|
||||
|
||||
$http->on('WorkerStart', function($server, $workerId) {
|
||||
Console::success('Worker '.++$workerId.' started successfully');
|
||||
$http->on('WorkerStart', function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on('BeforeReload', function($server, $workerId) {
|
||||
$http->on('BeforeReload', function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on('AfterReload', function($server, $workerId) {
|
||||
$http->on('AfterReload', function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
Files::load(__DIR__ . '/../public');
|
||||
Files::load(__DIR__ . '/../console');
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
$http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
$app = new App('UTC');
|
||||
|
||||
go(function() use ($register, $app) {
|
||||
go(function () use ($register, $app) {
|
||||
$pools = $register->get('pools'); /** @var Group $pools */
|
||||
App::setResource('pools', fn() => $pools);
|
||||
|
||||
// wait for database to be ready
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
@@ -66,42 +71,28 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
do {
|
||||
try {
|
||||
$attempts++;
|
||||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
|
||||
break; // leave the do-while if successful
|
||||
} catch(\Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('Failed to connect to database: '. $e->getMessage());
|
||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
App::setResource('db', fn() => $db);
|
||||
App::setResource('cache', fn() => $redis);
|
||||
|
||||
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
|
||||
|
||||
Console::success('[Setup] - Server database init started...');
|
||||
$collections = Config::getParam('collections', []); /** @var array $collections */
|
||||
|
||||
if(!$dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'))) {
|
||||
$redis->flushAll();
|
||||
|
||||
Console::success('[Setup] - Creating database: appwrite...');
|
||||
|
||||
$dbForConsole->create(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
}
|
||||
|
||||
try {
|
||||
Console::success('[Setup] - Creating metadata table: appwrite...');
|
||||
$dbForConsole->createMetadata();
|
||||
} catch (\Throwable $th) {
|
||||
$cache = $app->getResource('cache'); /** @var Utopia\Cache\Cache $cache */
|
||||
Console::success('[Setup] - Creating database: appwrite...');
|
||||
$dbForConsole->create();
|
||||
} catch (\Exception $e) {
|
||||
Console::success('[Setup] - Skip: metadata table already exists');
|
||||
}
|
||||
|
||||
if($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
$audit = new Audit($dbForConsole);
|
||||
$audit->setup();
|
||||
}
|
||||
@@ -111,13 +102,24 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
$adapter->setup();
|
||||
}
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
$consoleCollections = $collections['console'];
|
||||
foreach ($consoleCollections as $key => $collection) {
|
||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
if(!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
if (!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip to prevent 0.16 migration issues.
|
||||
*/
|
||||
if (in_array($key, ['cache', 'variables']) && $dbForConsole->exists($dbForConsole->getDefaultDatabase(), 'bucket_1')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
||||
|
||||
$attributes = [];
|
||||
@@ -125,7 +127,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'$id' => ID::custom($attribute['$id']),
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
@@ -139,7 +141,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
|
||||
foreach ($collection['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'$id' => ID::custom($index['$id']),
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
@@ -150,39 +152,42 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
$dbForConsole->createCollection($key, $attributes, $indexes);
|
||||
}
|
||||
|
||||
if($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDefaultDatabase(), 'bucket_1')) {
|
||||
Console::success('[Setup] - Creating default bucket...');
|
||||
$dbForConsole->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
'$collection' => 'buckets',
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'$id' => ID::custom('default'),
|
||||
'$collection' => ID::custom('buckets'),
|
||||
'name' => 'Default',
|
||||
'permission' => 'file',
|
||||
'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
|
||||
'allowedFileExtensions' => [],
|
||||
'enabled' => true,
|
||||
'compression' => 'gzip',
|
||||
'encryption' => true,
|
||||
'antivirus' => true,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'fileSecurity' => true,
|
||||
'$permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'search' => 'buckets Default',
|
||||
]));
|
||||
|
||||
$bucket = $dbForConsole->getDocument('buckets', 'default');
|
||||
|
||||
|
||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||
$files = $collections['files'] ?? [];
|
||||
if(empty($files)) {
|
||||
$files = $collections['buckets']['files'] ?? [];
|
||||
if (empty($files)) {
|
||||
throw new Exception('Files collection is not configured.');
|
||||
}
|
||||
|
||||
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
|
||||
foreach ($files['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
'$id' => ID::custom($attribute['$id']),
|
||||
'type' => $attribute['type'],
|
||||
'size' => $attribute['size'],
|
||||
'required' => $attribute['required'],
|
||||
@@ -193,25 +198,26 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
'format' => $attribute['format'] ?? ''
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
foreach ($files['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
'$id' => ID::custom($index['$id']),
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
}
|
||||
|
||||
$pools->reclaim();
|
||||
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)');
|
||||
|
||||
Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)');
|
||||
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");
|
||||
|
||||
// listen ctrl + c
|
||||
@@ -225,41 +231,37 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
$request = new Request($swooleRequest);
|
||||
$response = new Response($swooleResponse);
|
||||
|
||||
if(Files::isFileLoaded($request->getURI())) {
|
||||
if (Files::isFileLoaded($request->getURI())) {
|
||||
$time = (60 * 60 * 24 * 365 * 2); // 45 days cache
|
||||
|
||||
$response
|
||||
->setContentType(Files::getFileMimeType($request->getURI()))
|
||||
->addHeader('Cache-Control', 'public, max-age='.$time)
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
|
||||
->send(Files::getFileContents($request->getURI()))
|
||||
;
|
||||
->addHeader('Cache-Control', 'public, max-age=' . $time)
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
|
||||
->send(Files::getFileContents($request->getURI()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$app = new App('UTC');
|
||||
|
||||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
|
||||
App::setResource('db', fn() => $db);
|
||||
App::setResource('cache', fn() => $redis);
|
||||
$pools = $register->get('pools');
|
||||
App::setResource('pools', fn() => $pools);
|
||||
|
||||
try {
|
||||
Authorization::cleanRoles();
|
||||
Authorization::setRole('role:all');
|
||||
Authorization::setRole(Role::any()->toString());
|
||||
|
||||
$app->run($request, $response);
|
||||
} catch (\Throwable $th) {
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$logger = $app->getResource("logger");
|
||||
if($logger) {
|
||||
if ($logger) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $app->getResource('user');
|
||||
} catch(\Throwable $_th) {
|
||||
} catch (\Throwable $_th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
|
||||
@@ -268,7 +270,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
|
||||
$log = new Utopia\Logger\Log();
|
||||
|
||||
if(isset($user) && !$user->isEmpty()) {
|
||||
if (isset($user) && !$user->isEmpty()) {
|
||||
$log->setUser(new User($user->getId()));
|
||||
}
|
||||
|
||||
@@ -279,7 +281,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
$log->setMessage($th->getMessage());
|
||||
|
||||
$log->addTag('method', $route->getMethod());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('verboseType', get_class($th));
|
||||
$log->addTag('code', $th->getCode());
|
||||
// $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant
|
||||
@@ -298,30 +300,23 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
foreach($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
foreach ($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
$log->addBreadcrumb($loggerBreadcrumb);
|
||||
}
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Log pushed with status code: '.$responseCode);
|
||||
Console::info('Log pushed with status code: ' . $responseCode);
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: '.get_class($th));
|
||||
Console::error('[Error] Message: '.$th->getMessage());
|
||||
Console::error('[Error] File: '.$th->getFile());
|
||||
Console::error('[Error] Line: '.$th->getLine());
|
||||
|
||||
/**
|
||||
* Reset Database connection if PDOException was thrown.
|
||||
*/
|
||||
if ($th instanceof PDOException) {
|
||||
$db = null;
|
||||
}
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
|
||||
$swooleResponse->setStatusCode(500);
|
||||
|
||||
$output = ((App::isDevelopment())) ? [
|
||||
'message' => 'Error: '. $th->getMessage(),
|
||||
'message' => 'Error: ' . $th->getMessage(),
|
||||
'code' => 500,
|
||||
'file' => $th->getFile(),
|
||||
'line' => $th->getLine(),
|
||||
@@ -335,14 +330,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
||||
|
||||
$swooleResponse->end(\json_encode($output));
|
||||
} finally {
|
||||
/** @var PDOPool $dbPool */
|
||||
$dbPool = $register->get('dbPool');
|
||||
$dbPool->put($db);
|
||||
|
||||
/** @var RedisPool $redisPool */
|
||||
$redisPool = $register->get('redisPool');
|
||||
$redisPool->put($redis);
|
||||
$pools->reclaim();
|
||||
}
|
||||
});
|
||||
|
||||
$http->start();
|
||||
$http->start();
|
||||
|
||||
+663
-344
File diff suppressed because it is too large
Load Diff
+12
-8
@@ -2,27 +2,28 @@
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
*
|
||||
* Inializes both Appwrite API entry point, queue workers, and CLI tasks.
|
||||
* Set configuration, framework resources, app constants
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
}
|
||||
|
||||
use Utopia\Preloader\Preloader;
|
||||
|
||||
include __DIR__.'/controllers/general.php';
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
$preloader = new Preloader();
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
realpath(__DIR__ . '/../vendor/composer'),
|
||||
realpath(__DIR__ . '/../vendor/amphp'),
|
||||
realpath(__DIR__ . '/../vendor/felixfbecker'),
|
||||
@@ -34,8 +35,11 @@ foreach ([
|
||||
realpath(__DIR__ . '/../vendor/symfony'),
|
||||
realpath(__DIR__ . '/../vendor/mongodb'),
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload
|
||||
] as $key => $value) {
|
||||
if($value !== false) {
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/cache'), // TODO: remove memcached autoload
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/queue/src/Queue/Adapter/Workerman.php'), // TODO: remove workerman autoload
|
||||
] as $key => $value
|
||||
) {
|
||||
if ($value !== false) {
|
||||
$preloader->ignore($value);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user