mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
615
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a4c5bec27 | ||
|
|
97acbd42ba | ||
|
|
79e066a4d7 | ||
|
|
725dff7f0f | ||
|
|
358e096301 | ||
|
|
74c050ffb6 | ||
|
|
8376b176cb | ||
|
|
69ed3bb776 | ||
|
|
262b2e6cff | ||
|
|
acb9548e85 | ||
|
|
ee1ee7ecfe | ||
|
|
b14d76af0d | ||
|
|
f2ba319e6e | ||
|
|
f1877476d0 | ||
|
|
75b3e78098 | ||
|
|
82e16e6d9e | ||
|
|
63710e8ff8 | ||
|
|
794329be82 | ||
|
|
5549cd8900 | ||
|
|
a840d9de20 | ||
|
|
1a90c8aab5 | ||
|
|
7f2fdf623c | ||
|
|
39699d16ba | ||
|
|
8f391db39a | ||
|
|
2f9802afa2 | ||
|
|
3af1910cf9 | ||
|
|
796a845bcf | ||
|
|
56736a79e2 | ||
|
|
536319b440 | ||
|
|
ea35b6226d | ||
|
|
42cd1df77d | ||
|
|
994e2faa42 | ||
|
|
ba26dd6df5 | ||
|
|
0170b4c9e8 | ||
|
|
ab7041d653 | ||
|
|
1a56be197c | ||
|
|
b79b320414 | ||
|
|
1b04ae6d96 | ||
|
|
95e96b3bfb | ||
|
|
18e4be2eab | ||
|
|
7f05a74b31 | ||
|
|
733c5ad97d | ||
|
|
71b56fc504 | ||
|
|
62eb159e6a | ||
|
|
f61db29498 | ||
|
|
d8418a222c | ||
|
|
63c7f4c8bc | ||
|
|
b11ad4b1c9 | ||
|
|
93510d170e | ||
|
|
be3d18ab41 | ||
|
|
efc5b9e353 | ||
|
|
d86cf7b437 | ||
|
|
aabee0ac37 | ||
|
|
4e1168654d | ||
|
|
254018071a | ||
|
|
e904ab6232 | ||
|
|
fed6cefa81 | ||
|
|
640f09c49b | ||
|
|
06bb001d0b | ||
|
|
3730ff4895 | ||
|
|
05771c46d6 | ||
|
|
4ff0b6283a | ||
|
|
852a16d01b | ||
|
|
084b6c799a | ||
|
|
eddeac6e4f | ||
|
|
874f60dce6 | ||
|
|
d21814c51f | ||
|
|
65bcf7898e | ||
|
|
7e460f6155 | ||
|
|
1a271ff272 | ||
|
|
5963631c70 | ||
|
|
0fa5460a7e | ||
|
|
12e3d70032 | ||
|
|
5f766f6a13 | ||
|
|
7d27ceef43 | ||
|
|
9c3cfc4f83 | ||
|
|
b4d366eea1 | ||
|
|
8a4954d4e8 | ||
|
|
1943e00e40 | ||
|
|
f75acfa503 | ||
|
|
b69c40345f | ||
|
|
83dd6a9897 | ||
|
|
75f543c6b0 | ||
|
|
232f99c6c9 | ||
|
|
faa1b0a59b | ||
|
|
fe210e0cf0 | ||
|
|
f85bf337b7 | ||
|
|
06020e2b62 | ||
|
|
8ff4d60dbb | ||
|
|
43cd7ac8b3 | ||
|
|
393c4012d9 | ||
|
|
c6a862a0a3 | ||
|
|
1d983a949e | ||
|
|
5e30fc5350 | ||
|
|
a5457da91c | ||
|
|
a37a6d9d7c | ||
|
|
c1ee2f2b41 | ||
|
|
6a584c83c1 | ||
|
|
777fb3fcdd | ||
|
|
6ea20659e6 | ||
|
|
dade177af2 | ||
|
|
97ffbbc44b | ||
|
|
f07c351c94 | ||
|
|
a42522feaa | ||
|
|
9400fdbbe5 | ||
|
|
92b696ea21 | ||
|
|
50ec55d595 | ||
|
|
a7fc8cf84b | ||
|
|
794c6536fb | ||
|
|
2e30c1ba1f | ||
|
|
654d178cf2 | ||
|
|
02e740f8b0 | ||
|
|
62b639ce48 | ||
|
|
825e9639b1 | ||
|
|
9a8a2b5a49 | ||
|
|
4428ed1f7d | ||
|
|
de5f33f206 | ||
|
|
9a44e8130f | ||
|
|
2f3c43d0db | ||
|
|
77d27bdd20 | ||
|
|
87a9d72523 | ||
|
|
f6674f07e4 | ||
|
|
049ad14121 | ||
|
|
a0a82b86dc | ||
|
|
326e62334f | ||
|
|
b29b5da006 | ||
|
|
46039ded2f | ||
|
|
dfce78c421 | ||
|
|
ce3bff13dd | ||
|
|
a031682596 | ||
|
|
9b8dd02a88 | ||
|
|
bbf37e0a0e | ||
|
|
826b034cd2 | ||
|
|
9ee9a8e0ed | ||
|
|
dfc2e87170 | ||
|
|
520b96f0d9 | ||
|
|
ea30d976a8 | ||
|
|
9b8e4dd862 | ||
|
|
ef7b9537f7 | ||
|
|
03c9e57773 | ||
|
|
54413f4c5e | ||
|
|
a2abd982cf | ||
|
|
f5a0008445 | ||
|
|
64183f2946 | ||
|
|
d9eb942e8c | ||
|
|
84e1b050a9 | ||
|
|
e94438927e | ||
|
|
20ad91a906 | ||
|
|
2ae61164d4 | ||
|
|
774a6af333 | ||
|
|
8e03db6c9e | ||
|
|
5dd4348d5c | ||
|
|
64e2f904c5 | ||
|
|
07ceb1991d | ||
|
|
60f34ced91 | ||
|
|
084dcdffd9 | ||
|
|
c303aa57ce | ||
|
|
2384113a04 | ||
|
|
ef37139592 | ||
|
|
8b8f632abe | ||
|
|
7e00bc493c | ||
|
|
8d47d62d7a | ||
|
|
ef030f4a08 | ||
|
|
580204912c | ||
|
|
f91e8be136 | ||
|
|
d75b601a7b | ||
|
|
72d2ba0159 | ||
|
|
dc71c50123 | ||
|
|
d6e7db8900 | ||
|
|
559396295f | ||
|
|
dc6143c773 | ||
|
|
a1c621fab2 | ||
|
|
2510cb9687 | ||
|
|
4efa0a7231 | ||
|
|
4afd9771c6 | ||
|
|
dd75081609 | ||
|
|
a3c176a2bd | ||
|
|
8081759c5c | ||
|
|
bf3b21cdb1 | ||
|
|
26a822e4b2 | ||
|
|
cede07b520 | ||
|
|
92daef51f0 | ||
|
|
0dbb9dadf6 | ||
|
|
496cfa92bc | ||
|
|
ac51fcbf4e | ||
|
|
93d00abfe1 | ||
|
|
d79ab99202 | ||
|
|
1e111294df | ||
|
|
1752e8ed83 | ||
|
|
22598ae41d | ||
|
|
a89f591f63 | ||
|
|
ac9ac3e279 | ||
|
|
859a9c72e1 | ||
|
|
19928bd7dc | ||
|
|
88b3791a24 | ||
|
|
765a0fa891 | ||
|
|
fac42f7ad5 | ||
|
|
d7c163be95 | ||
|
|
16934af49f | ||
|
|
29f660cfd6 | ||
|
|
743ded1e79 | ||
|
|
88ae06af57 | ||
|
|
a54f84fce8 | ||
|
|
f830918c7c | ||
|
|
2ba84bf93f | ||
|
|
474e04479f | ||
|
|
40beecdddc | ||
|
|
427e781b5b | ||
|
|
9e909ee3fc | ||
|
|
eb7daf3edd | ||
|
|
a56086f8bf | ||
|
|
ae835f6cf0 | ||
|
|
666e8f3ec9 | ||
|
|
aeebdadae1 | ||
|
|
0a25463b8d | ||
|
|
7e3b693216 | ||
|
|
a5fc9463c7 | ||
|
|
08ec153585 | ||
|
|
c8f4667b6a | ||
|
|
f9bc5d0cce | ||
|
|
7397d52c0c | ||
|
|
8661ae6840 | ||
|
|
498c1456ed | ||
|
|
2917c66202 | ||
|
|
26c99dca84 | ||
|
|
67d6404c70 | ||
|
|
1394b4349c | ||
|
|
ede068d5f4 | ||
|
|
1c6204118f | ||
|
|
cdc3cb23ec | ||
|
|
9d4806c792 | ||
|
|
54d877fe3e | ||
|
|
c3df0a71e3 | ||
|
|
732e74f114 | ||
|
|
d941d18e8c | ||
|
|
3ccd49e4a5 | ||
|
|
2d761b48d8 | ||
|
|
556bcbf33b | ||
|
|
e6a53a8c15 | ||
|
|
f5f37d6a79 | ||
|
|
f0d8c7382b | ||
|
|
fb19f8d6a6 | ||
|
|
68de687bdf | ||
|
|
a7c1bd94ed | ||
|
|
95aab45cc2 | ||
|
|
4a0d33213a | ||
|
|
322b726196 | ||
|
|
cd25a79694 | ||
|
|
47395c5442 | ||
|
|
d13e8641fd | ||
|
|
acd1dd2479 | ||
|
|
3cc1fb6fb4 | ||
|
|
3932b8bdc1 | ||
|
|
3bb4fde7e6 | ||
|
|
165caef9ab | ||
|
|
055411fbe4 | ||
|
|
63a91e30fa | ||
|
|
f7749bbbef | ||
|
|
c0b574fe9b | ||
|
|
34b8453666 | ||
|
|
ae1eb53e31 | ||
|
|
3b87b42dac | ||
|
|
db8010f7c6 | ||
|
|
c24cebf6ce | ||
|
|
262b152cd9 | ||
|
|
2e4a8dec78 | ||
|
|
2ea18c4761 | ||
|
|
28ef7da231 | ||
|
|
76b154bfce | ||
|
|
c2a60a8569 | ||
|
|
71103b037f | ||
|
|
9459c87b50 | ||
|
|
e5e7c50ca4 | ||
|
|
30cb7e8ff2 | ||
|
|
2f2a743a7f | ||
|
|
feffab137f | ||
|
|
6155fad3e0 | ||
|
|
d1978b102f | ||
|
|
b1854fee10 | ||
|
|
62b1c19357 | ||
|
|
e1dedd2fbb | ||
|
|
51e89f5110 | ||
|
|
81a7ba536c | ||
|
|
fb2649ab41 | ||
|
|
b746642979 | ||
|
|
a6bcc3ce97 | ||
|
|
b30d9eb8ad | ||
|
|
28339f0ed7 | ||
|
|
da9d442473 | ||
|
|
47a6168137 | ||
|
|
9222fec7f8 | ||
|
|
94f3cf7baf | ||
|
|
399afa1b14 | ||
|
|
410388a587 | ||
|
|
4bd08642be | ||
|
|
3ab8503f61 | ||
|
|
75320db428 | ||
|
|
43f0dc0846 | ||
|
|
a10882d7ee | ||
|
|
ec05a3d668 | ||
|
|
3531d223e2 | ||
|
|
e87fe7467e | ||
|
|
f6fbc253f7 | ||
|
|
313444cb12 | ||
|
|
bd58ba32c8 | ||
|
|
80241e37c3 | ||
|
|
4b909ed7dc | ||
|
|
f9ce932ac1 | ||
|
|
f67870925f | ||
|
|
f416e39098 | ||
|
|
c4142565aa | ||
|
|
81bfa5c48d | ||
|
|
0106dc7803 | ||
|
|
3b0d760065 | ||
|
|
e0369ec14f | ||
|
|
c78701ded7 | ||
|
|
adb861b785 | ||
|
|
aae3b40d16 | ||
|
|
6415009f07 | ||
|
|
b46eebb23c | ||
|
|
86a1fbe86a | ||
|
|
14e4854947 | ||
|
|
1c1f9aee2c | ||
|
|
084b62116f | ||
|
|
3d6351bbe0 | ||
|
|
187f4c841e | ||
|
|
15a0c28093 | ||
|
|
c440a3933b | ||
|
|
11477d67c9 | ||
|
|
f1989d0aba | ||
|
|
75219b800a | ||
|
|
4c3fa5f246 | ||
|
|
06ed493cfe | ||
|
|
7be0fbf616 | ||
|
|
6cf53bbb81 | ||
|
|
3a766bf970 | ||
|
|
f96291af88 | ||
|
|
8557a9e1b1 | ||
|
|
e8af2338ec | ||
|
|
aec9d91a68 | ||
|
|
950255fab9 | ||
|
|
7c9d6ccde2 | ||
|
|
5c286c3334 | ||
|
|
f8b27c0b76 | ||
|
|
6e5d4fb358 | ||
|
|
5e2abc539f | ||
|
|
2f03a33c15 | ||
|
|
7ce0e3c38c | ||
|
|
e269614a51 | ||
|
|
21c224709f | ||
|
|
ac8a29f047 | ||
|
|
681f4b9eda | ||
|
|
2b46bd2643 | ||
|
|
a322b022eb | ||
|
|
0dc43432e2 | ||
|
|
27de266cb0 | ||
|
|
2e048b0895 | ||
|
|
f8bf532fc0 | ||
|
|
210349dc70 | ||
|
|
eaa4795eb6 | ||
|
|
293f75954d | ||
|
|
f93eb2072c | ||
|
|
0a3f284e14 | ||
|
|
11ed6b8f47 | ||
|
|
8dd99b7d2a | ||
|
|
df99907120 | ||
|
|
7ac83d7a9f | ||
|
|
f2445c2a37 | ||
|
|
efb30e7f8e | ||
|
|
719a771613 | ||
|
|
e0131aa749 | ||
|
|
5882fda501 | ||
|
|
eeed8dbc16 | ||
|
|
7239b9d7cc | ||
|
|
480c7eac21 | ||
|
|
412c75ebdd | ||
|
|
f33e4b6f5a | ||
|
|
30f34d5764 | ||
|
|
2e62837de6 | ||
|
|
3d319e25a9 | ||
|
|
36c9c002d1 | ||
|
|
7019cea5fa | ||
|
|
d30af2c561 | ||
|
|
d272a474ab | ||
|
|
66c4003789 | ||
|
|
c5d4378069 | ||
|
|
58dc4a0280 | ||
|
|
3ed5e1b65a | ||
|
|
2ad3b68821 | ||
|
|
f51900a69c | ||
|
|
0fe072b17b | ||
|
|
124cff7bf2 | ||
|
|
3aafceab40 | ||
|
|
3b961d18f9 | ||
|
|
e311790365 | ||
|
|
47f4e67a52 | ||
|
|
9f9ce61c3a | ||
|
|
f9430d4d20 | ||
|
|
d5f1b56b90 | ||
|
|
1946df83a7 | ||
|
|
59d87a8af1 | ||
|
|
7be0f4a193 | ||
|
|
fcd920460f | ||
|
|
0124d5254c | ||
|
|
246dc07018 | ||
|
|
3ee67e620c | ||
|
|
9a94023bde | ||
|
|
70f1b0c2fc | ||
|
|
7009d1edf1 | ||
|
|
d652a17fd7 | ||
|
|
e49d9f39a8 | ||
|
|
4a3ea540bf | ||
|
|
04f9e9b62a | ||
|
|
76a0167700 | ||
|
|
aa130075e8 | ||
|
|
dd26a564dc | ||
|
|
652ab4946d | ||
|
|
efe24db5a1 | ||
|
|
0f8ce0e93e | ||
|
|
9089eb4228 | ||
|
|
8a260ceece | ||
|
|
426bcffd1a | ||
|
|
9c37c3a740 | ||
|
|
3c9ea6caba | ||
|
|
857046ecb8 | ||
|
|
f196cfe993 | ||
|
|
140b850cef | ||
|
|
0929ba995a | ||
|
|
6bf9adfb51 | ||
|
|
96956dbda5 | ||
|
|
15cc64e68b | ||
|
|
65d4bcef47 | ||
|
|
5f39affe74 | ||
|
|
ef21ab7f3b | ||
|
|
554a24e44c | ||
|
|
ee731b8530 | ||
|
|
dd3f2c6538 | ||
|
|
5a4958090e | ||
|
|
cd4f78d619 | ||
|
|
eb46ba354a | ||
|
|
f0b3c95035 | ||
|
|
e15fdc493c | ||
|
|
fdff1664ea | ||
|
|
4bdc9b0c84 | ||
|
|
563031f214 | ||
|
|
33273c1be1 | ||
|
|
1b95790de3 | ||
|
|
2ef1cf5da0 | ||
|
|
4a117adb28 | ||
|
|
86bc775791 | ||
|
|
9a57b09b92 | ||
|
|
36e3d9c603 | ||
|
|
df4c36d71b | ||
|
|
793eb53790 | ||
|
|
6b568e8b7f | ||
|
|
fec74ee9e4 | ||
|
|
10ef570485 | ||
|
|
d719fd9be4 | ||
|
|
135023b3ed | ||
|
|
2bd2092263 | ||
|
|
eee7d0422c | ||
|
|
0d314c00a6 | ||
|
|
a5fc2e0ef7 | ||
|
|
e168ab9ffc | ||
|
|
644c18fc69 | ||
|
|
57717f4690 | ||
|
|
c46a9bdb45 | ||
|
|
d431b53434 | ||
|
|
18d9ebaab3 | ||
|
|
40e343d17d | ||
|
|
f6122157c7 | ||
|
|
cbaad2dfb9 | ||
|
|
c509ec56bd | ||
|
|
a25b786fca | ||
|
|
06224aa34e | ||
|
|
4bcbd49d6d | ||
|
|
09e2ded206 | ||
|
|
4a8c063d51 | ||
|
|
2e9e6d38e2 | ||
|
|
53917d6d90 | ||
|
|
d20714eed6 | ||
|
|
2f328bf4cf | ||
|
|
e3c4cdb040 | ||
|
|
c8cf645414 | ||
|
|
aa47d9f07a | ||
|
|
338559967a | ||
|
|
a736a42e26 | ||
|
|
00c6549610 | ||
|
|
510d871379 | ||
|
|
43e2d7454a | ||
|
|
cb359e40ba | ||
|
|
7836e48559 | ||
|
|
d95cdc3296 | ||
|
|
2ad0428727 | ||
|
|
ba74ffd4bf | ||
|
|
b88cd8e544 | ||
|
|
a031b54feb | ||
|
|
081cf5c6aa | ||
|
|
6874b4f6fb | ||
|
|
eeba650f6c | ||
|
|
54b07e6b99 | ||
|
|
dc8b9478ee | ||
|
|
d97b02add9 | ||
|
|
8427ade0fa | ||
|
|
44d6a0dea1 | ||
|
|
f710ce266b | ||
|
|
45dc9a39d9 | ||
|
|
d703a118c4 | ||
|
|
c20e580a9e | ||
|
|
9b4de5d7bf | ||
|
|
7c9f396b40 | ||
|
|
31b9f2b4f9 | ||
|
|
6ff15f9b53 | ||
|
|
1ebd6c252c | ||
|
|
8424351d49 | ||
|
|
0c4e597819 | ||
|
|
d1917d1a2b | ||
|
|
bb3503f0c4 | ||
|
|
7c22776254 | ||
|
|
2294586371 | ||
|
|
b7f519f86e | ||
|
|
febd5294f4 | ||
|
|
690a23fe89 | ||
|
|
39c1a88dae | ||
|
|
d0808e0c1a | ||
|
|
34fd22e4e9 | ||
|
|
d4fb0d37c2 | ||
|
|
fd02ca00f0 | ||
|
|
8a53d02d0c | ||
|
|
58ee53876b | ||
|
|
853b730def | ||
|
|
be5fdbfd4f | ||
|
|
e6d123b5e8 | ||
|
|
d8a844a2f9 | ||
|
|
dd87cdbebf | ||
|
|
4c1881324e | ||
|
|
bd27a36d16 | ||
|
|
2f999ba3db | ||
|
|
d8ec390cd6 | ||
|
|
7a0e7bfb95 | ||
|
|
da7ae6b953 | ||
|
|
2dead545f0 | ||
|
|
9c566ec17c | ||
|
|
385fe303cc | ||
|
|
81e3709d25 | ||
|
|
7f17efcdd8 | ||
|
|
a566ac1078 | ||
|
|
44e76b4eaa | ||
|
|
e7e1cfa12e | ||
|
|
77a70cad71 | ||
|
|
0c2db53ee8 | ||
|
|
83807ed247 | ||
|
|
302a135e5a | ||
|
|
6b2160f388 | ||
|
|
86a68c50bc | ||
|
|
e2e7d6578c | ||
|
|
a171e17666 | ||
|
|
d8610e0f2a | ||
|
|
93222bf3f9 | ||
|
|
ea6765c68a | ||
|
|
6ac81dea19 | ||
|
|
e4dd9bc9f9 | ||
|
|
03d4d4c8fa | ||
|
|
fcc51d3352 | ||
|
|
08630cd686 | ||
|
|
9295a4e7ce | ||
|
|
826a1a02a2 | ||
|
|
4ed08cefa7 | ||
|
|
1a33a0c4d1 | ||
|
|
4d3d710211 | ||
|
|
ac93e70dd2 | ||
|
|
dd3ffbb391 | ||
|
|
9d1d101586 | ||
|
|
32da7b8fe2 | ||
|
|
41484113a0 | ||
|
|
5d6a8be66d | ||
|
|
aba3a31663 | ||
|
|
8950f71b0a | ||
|
|
54d3668693 | ||
|
|
b16169f867 | ||
|
|
bbd589f6a8 | ||
|
|
765d8241f2 | ||
|
|
8e041ddeb6 | ||
|
|
a4a7fe0c48 | ||
|
|
a247fc3b48 | ||
|
|
d876085f6d | ||
|
|
bba9c3d57a | ||
|
|
944e971124 | ||
|
|
e512ab1816 | ||
|
|
83b5aecbfd | ||
|
|
2f9d00fc07 | ||
|
|
23b57692ec | ||
|
|
8bda7f1e1e | ||
|
|
12bddc3b1f | ||
|
|
8f5fa84996 | ||
|
|
3af34f078c | ||
|
|
57e596fea2 | ||
|
|
9793cf4ece | ||
|
|
a4bdc23aef | ||
|
|
4240089d41 | ||
|
|
09ebfac307 | ||
|
|
89b6291488 | ||
|
|
1434b6bbff | ||
|
|
de164ff936 | ||
|
|
a8e2fb1ee5 | ||
|
|
7da8b44c63 | ||
|
|
8f0c1a4224 | ||
|
|
c57cb3d2d9 | ||
|
|
5172189e36 | ||
|
|
6c6723e181 | ||
|
|
5b0a05b4b3 | ||
|
|
87cbd8fbf3 | ||
|
|
9060250806 | ||
|
|
bc63c3b383 | ||
|
|
a503803725 |
@@ -15,15 +15,15 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
|
||||
_APP_EMAIL_SECURITY=security@appwrite.io
|
||||
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
|
||||
_APP_SYSTEM_RESPONSE_FORMAT=
|
||||
_APP_CUSTOM_DOMAIN_DENY_LIST=
|
||||
_APP_OPTIONS_ABUSE=disabled
|
||||
_APP_OPTIONS_ROUTER_PROTECTION=disabled
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
|
||||
_APP_OPTIONS_COMPUTE_FORCE_HTTPS=disabled
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key
|
||||
_APP_DOMAIN=traefik
|
||||
_APP_DOMAIN_FUNCTIONS=functions.localhost
|
||||
_APP_DOMAIN_TARGET=localhost
|
||||
_APP_DOMAIN_SITES=sites.localhost
|
||||
_APP_DOMAIN_TARGET=test.appwrite.io
|
||||
_APP_RULES_FORMAT=md5
|
||||
_APP_REDIS_HOST=redis
|
||||
_APP_REDIS_PORT=6379
|
||||
@@ -70,24 +70,25 @@ _APP_SMS_FROM=+123456789
|
||||
_APP_SMS_PROJECTS_DENY_LIST=
|
||||
_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_CPUS=8
|
||||
_APP_FUNCTIONS_MEMORY=8192
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=600
|
||||
_APP_FUNCTIONS_MAINTENANCE_INTERVAL=600
|
||||
_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes
|
||||
_APP_COMPUTE_SIZE_LIMIT=30000000
|
||||
_APP_COMPUTE_TIMEOUT=900
|
||||
_APP_COMPUTE_BUILD_TIMEOUT=900
|
||||
_APP_COMPUTE_CPUS=8
|
||||
_APP_COMPUTE_MEMORY=8192
|
||||
_APP_COMPUTE_INACTIVE_THRESHOLD=600
|
||||
_APP_COMPUTE_MAINTENANCE_INTERVAL=600
|
||||
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
|
||||
_APP_EXECUTOR_SECRET=your-secret-key
|
||||
_APP_EXECUTOR_HOST=http://proxy/v1
|
||||
_APP_EXECUTOR_HOST=http://exc1/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
|
||||
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29
|
||||
_APP_SITES_FRAMEWORKS=sveltekit,nextjs,nuxt,astro,remix,flutter,other,react,vue,analog,angular,vite
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_START_TIME=12:00
|
||||
_APP_MAINTENANCE_DELAY=
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800
|
||||
_APP_USAGE_AGGREGATION_INTERVAL=30
|
||||
_APP_STATS_RESOURCES_INTERVAL=3600
|
||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
name: "Static code analysis"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run CodeQL
|
||||
run: |
|
||||
docker run --rm -v $PWD:/app composer:2.6 sh -c \
|
||||
"composer install --profile --ignore-platform-reqs && composer check"
|
||||
@@ -90,6 +90,9 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
|
||||
- name: Logs
|
||||
run: docker compose logs appwrite
|
||||
|
||||
- name: Doctor
|
||||
run: docker compose exec -T appwrite doctor
|
||||
|
||||
@@ -142,6 +145,8 @@ jobs:
|
||||
Locale,
|
||||
Projects,
|
||||
Realtime,
|
||||
Sites,
|
||||
Proxy,
|
||||
Storage,
|
||||
Teams,
|
||||
Users,
|
||||
@@ -167,6 +172,14 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Wait for Open Runtimes
|
||||
timeout-minutes: 3
|
||||
run: |
|
||||
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
|
||||
echo "Waiting for Executor to come online"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
- name: Run ${{ matrix.service }} tests with Project table mode
|
||||
run: |
|
||||
echo "Using project tables"
|
||||
@@ -199,6 +212,8 @@ jobs:
|
||||
Locale,
|
||||
Projects,
|
||||
Realtime,
|
||||
Sites,
|
||||
Proxy,
|
||||
Storage,
|
||||
Teams,
|
||||
Users,
|
||||
@@ -229,6 +244,14 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Wait for Open Runtimes
|
||||
timeout-minutes: 3
|
||||
run: |
|
||||
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
|
||||
echo "Waiting for Executor to come online"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
- name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode
|
||||
run: |
|
||||
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
|
||||
@@ -240,7 +263,7 @@ jobs:
|
||||
export _APP_DATABASE_SHARED_TABLES=database_db_main
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=
|
||||
fi
|
||||
|
||||
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/node_modules/
|
||||
/tests/resources/storage/
|
||||
/tests/resources/functions/**/code.tar.gz
|
||||
/tests/resources/sites/**/code.tar.gz
|
||||
/app/sdks/*
|
||||
/.idea/
|
||||
!/.idea/workspace.xml
|
||||
|
||||
+8
-1
@@ -7,7 +7,6 @@ tasks:
|
||||
docker pull composer
|
||||
command: |
|
||||
docker run --rm --interactive --tty \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--volume $PWD:/app \
|
||||
composer install \
|
||||
--ignore-platform-reqs \
|
||||
@@ -24,3 +23,11 @@ vscode:
|
||||
extensions:
|
||||
- ms-azuretools.vscode-docker
|
||||
- zobo.php-intellisense
|
||||
|
||||
github:
|
||||
# https://www.gitpod.io/docs/prebuilds#github-specific-configuration
|
||||
prebuilds:
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a check to pull requests (defaults to true)
|
||||
addCheck: false
|
||||
|
||||
@@ -163,6 +163,28 @@ Other containes should be named the same as their service, for example `redis` s
|
||||
- [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.)
|
||||
|
||||
## Modules
|
||||
|
||||
As Appwrite grows, we noticed approach of having all service endpoints in `app/controllers/api/[service].php` is not maintainable. Not only it creates massive files, it also doesnt contain all product's features such as workers or tasks. While there might still be some occurances of those controller files, we avoid it in all new development, and gradually migrate existing controllers to **HTTP modules**.
|
||||
|
||||
### HTTP Endpoints
|
||||
|
||||
Every endpoint file follows below structure, making it consistent with HTTP REST endpoint path:
|
||||
|
||||
```
|
||||
src/Appwrite/Platform/Modules/[service]/Http/[resource]/[action].php
|
||||
```
|
||||
|
||||
Tips and tricks:
|
||||
|
||||
1. If endpoint doesn't have resource, use service name as resource name too
|
||||
> Example: `Modules/Sites/Http/Sites/Get.php`
|
||||
|
||||
2. If there are multiple resources, use then all in folder structure
|
||||
> Example: `Modules/Sites/Http/Deployments/Builds/Create.php`
|
||||
|
||||
3. Action can only be `Get`, `Create`, `Update`, `Delete` or `XList`
|
||||
|
||||
## Architecture
|
||||
|
||||
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.
|
||||
|
||||
@@ -68,6 +68,7 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/sdks && \
|
||||
chmod +x /usr/local/bin/specs && \
|
||||
chmod +x /usr/local/bin/ssl && \
|
||||
chmod +x /usr/local/bin/screenshot && \
|
||||
chmod +x /usr/local/bin/test && \
|
||||
chmod +x /usr/local/bin/upgrade && \
|
||||
chmod +x /usr/local/bin/vars && \
|
||||
@@ -86,6 +87,7 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-migrations && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/worker-stats-usage && \
|
||||
chmod +x /usr/local/bin/worker-stats-usage-dump && \
|
||||
chmod +x /usr/local/bin/stats-resources && \
|
||||
chmod +x /usr/local/bin/worker-stats-resources
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
> [Get started with Appwrite](https://apwr.dev/appcloud)
|
||||
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) :rocket:
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
@@ -24,9 +24,11 @@
|
||||
|
||||
English | [简体中文](README-CN.md)
|
||||
|
||||
[**Announcing Appwrite Cloud Public Beta! Sign up today!**](https://cloud.appwrite.io)
|
||||
|
||||
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 and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and [more services](https://appwrite.io/docs).
|
||||
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs).
|
||||
|
||||
<p align="center">
|
||||
<br />
|
||||
@@ -50,7 +52,7 @@ Table of Contents:
|
||||
- [Upgrade from an Older Version](#upgrade-from-an-older-version)
|
||||
- [One-Click Setups](#one-click-setups)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Products](#products)
|
||||
- [Services](#services)
|
||||
- [SDKs](#sdks)
|
||||
- [Client](#client)
|
||||
- [Server](#server)
|
||||
|
||||
+13
-43
@@ -9,8 +9,6 @@ use Appwrite\Event\StatsResources;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
use Executor\Executor;
|
||||
use Swoole\Timer;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\CLI;
|
||||
@@ -26,12 +24,9 @@ use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
|
||||
use function Swoole\Coroutine\run;
|
||||
|
||||
// Overwriting runtimes to be architecture agnostic for CLI
|
||||
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
|
||||
// overwriting runtimes to be architectur agnostic for CLI
|
||||
Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
|
||||
|
||||
// require controllers after overwriting runtimes
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
@@ -48,7 +43,8 @@ CLI::setResource('cache', function ($pools) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource();
|
||||
->getResource()
|
||||
;
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
@@ -191,7 +187,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
// set tenant
|
||||
@@ -203,7 +199,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
CLI::setResource('queueForStatsUsage', function (Connection $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
@@ -223,13 +219,6 @@ CLI::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
}, ['publisher']);
|
||||
CLI::setResource('logError', function (Registry $register) {
|
||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
Console::error('[Error] Message: ' . $error->getMessage());
|
||||
Console::error('[Error] File: ' . $error->getFile());
|
||||
Console::error('[Error] Line: ' . $error->getLine());
|
||||
Console::error('[Error] Trace: ' . $error->getTraceAsString());
|
||||
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
@@ -248,7 +237,6 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
@@ -262,40 +250,22 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
};
|
||||
}, ['register']);
|
||||
|
||||
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
|
||||
CLI::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
|
||||
if (!isset($args[0])) {
|
||||
Console::error('Missing task name');
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
\array_shift($args);
|
||||
$taskName = $args[0];
|
||||
$platform->init(Service::TYPE_TASK);
|
||||
|
||||
$cli = $platform->getCli();
|
||||
|
||||
$cli
|
||||
->error()
|
||||
->inject('error')
|
||||
->inject('logError')
|
||||
->action(function (Throwable $error, callable $logError) use ($taskName) {
|
||||
call_user_func_array($logError, [
|
||||
$error,
|
||||
'Task',
|
||||
$taskName,
|
||||
]);
|
||||
|
||||
Timer::clearAll();
|
||||
->action(function (Throwable $error) {
|
||||
Console::error($error->getMessage());
|
||||
});
|
||||
|
||||
$cli->shutdown()->action(fn () => Timer::clearAll());
|
||||
|
||||
run($cli->run(...));
|
||||
$cli->run();
|
||||
|
||||
@@ -1038,7 +1038,7 @@ return [
|
||||
'$id' => ID::custom('providerUid'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048, // Decrease to 128 as in index length?
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
@@ -1107,14 +1107,14 @@ return [
|
||||
'$id' => ID::custom('_key_userInternalId_provider_providerUid'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['userInternalId', 'provider', 'providerUid'],
|
||||
'lengths' => [11, 128, 128], // providerUid is length 2000!
|
||||
'lengths' => [11, 128, 128],
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_provider_providerUid'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['provider', 'providerUid'],
|
||||
'lengths' => [128, 128], // providerUid is length 2000!
|
||||
'lengths' => [128, 128],
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
@@ -2352,20 +2352,6 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_expired'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['expired'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_session_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['sessionInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -356,21 +356,7 @@ return [
|
||||
'attributes' => ['pingedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_database'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['database'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region_accessed_at'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'accessedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
@@ -494,20 +480,6 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_id_region'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectId', 'region'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region_rt_active'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'resourceType', 'active'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1041,10 +1013,10 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceType'),
|
||||
'$id' => ID::custom('type'), // 'api', 'redirect', 'deployment' (site or function)
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 100,
|
||||
'size' => 32,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
@@ -1052,10 +1024,32 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceInternalId'),
|
||||
'$id' => ID::custom('trigger'), // 'manual', 'deployment', '' (empty)
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'size' => 32,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('redirectUrl'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('redirectStatusCode'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
@@ -1063,13 +1057,68 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceId'),
|
||||
'$id' => ID::custom('deploymentResourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 32,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentResourceId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentResourceInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentVcsProviderBranch'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
@@ -1096,29 +1145,25 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('owner'),
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '', // "Appwrite" or empty string
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('region'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_search'),
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_domain'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
@@ -1141,47 +1186,61 @@ return [
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_resourceInternalId',
|
||||
'$id' => '_key_type',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceInternalId'],
|
||||
'attributes' => ['type'],
|
||||
'lengths' => [32],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_trigger',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['trigger'],
|
||||
'lengths' => [32],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deploymentResourceType',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentResourceType'],
|
||||
'lengths' => [32],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deploymentResourceId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentResourceId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_resourceId',
|
||||
'$id' => '_key_deploymentResourceInternalId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceId'],
|
||||
'attributes' => ['deploymentResourceInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_resourceType',
|
||||
'$id' => '_key_deploymentId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceType'],
|
||||
'lengths' => [],
|
||||
'attributes' => ['deploymentId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_owner'),
|
||||
'$id' => '_key_deploymentInternalId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['owner'],
|
||||
'lengths' => [16],
|
||||
'attributes' => ['deploymentInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region'),
|
||||
'$id' => '_key_deploymentVcsProviderBranch',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region'],
|
||||
'lengths' => [16],
|
||||
'attributes' => ['deploymentVcsProviderBranch'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_riid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1477,14 +1536,7 @@ return [
|
||||
'attributes' => ['resourceType'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_riid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1632,13 +1684,6 @@ return [
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_prid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'providerRepositoryId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
+525
-233
@@ -569,7 +569,7 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deployment'),
|
||||
'$id' => ID::custom('deploymentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
@@ -674,7 +674,7 @@ return [
|
||||
'size' => 8,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 'v4',
|
||||
'default' => 'v5',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
@@ -708,7 +708,7 @@ return [
|
||||
'size' => 128,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => APP_FUNCTION_SPECIFICATION_DEFAULT,
|
||||
'default' => APP_COMPUTE_SPECIFICATION_DEFAULT,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
@@ -788,15 +788,372 @@ return [
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_deployment'),
|
||||
'$id' => ID::custom('_key_deploymentId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deployment'],
|
||||
'attributes' => ['deploymentId'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
'sites' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('sites'),
|
||||
'name' => 'Sites',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('name'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('enabled'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('live'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('installationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('installationInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerRepositoryId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('repositoryId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('repositoryInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerBranch'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerRootDirectory'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('providerSilentMode'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
],
|
||||
// [
|
||||
// '$id' => ID::custom('logging'),
|
||||
// 'type' => Database::VAR_BOOLEAN,
|
||||
// 'signed' => true,
|
||||
// 'size' => 0,
|
||||
// 'format' => '',
|
||||
// 'filters' => [],
|
||||
// 'required' => true,
|
||||
// 'array' => false,
|
||||
// ],
|
||||
[
|
||||
'$id' => ID::custom('framework'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('outputDirectory'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('buildCommand'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('installCommand'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('fallbackFile'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vars'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryVariables'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('varsProject'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryProjectVariables'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('timeout'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('specification'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => APP_COMPUTE_SPECIFICATION_DEFAULT,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildRuntime'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('adapter'), // ssr or static
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_search'),
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_name'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['name'],
|
||||
'lengths' => [256],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_enabled'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['enabled'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_installationId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['installationId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_installationInternalId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['installationInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_providerRepositoryId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['providerRepositoryId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_repositoryId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['repositoryId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_repositoryInternalId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['repositoryInternalId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_framework'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['framework'],
|
||||
'lengths' => [64],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_deploymentId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentId'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
],
|
||||
],
|
||||
'deployments' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('deployments'),
|
||||
@@ -835,28 +1192,6 @@ return [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('entrypoint'),
|
||||
@@ -870,17 +1205,28 @@ return [
|
||||
],
|
||||
[
|
||||
'array' => false,
|
||||
'$id' => ID::custom('commands'),
|
||||
'$id' => ID::custom('buildCommands'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('path'),
|
||||
'array' => false,
|
||||
'$id' => ID::custom('buildOutput'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('sourcePath'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
@@ -894,7 +1240,7 @@ return [
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'size' => 32,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
@@ -1073,7 +1419,7 @@ return [
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('size'),
|
||||
'$id' => ID::custom('sourceSize'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
@@ -1084,7 +1430,7 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('metadata'),
|
||||
'$id' => ID::custom('sourceMetadata'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2
|
||||
@@ -1095,7 +1441,7 @@ return [
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('chunksTotal'),
|
||||
'$id' => ID::custom('sourceChunksTotal'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
@@ -1106,7 +1452,7 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('chunksUploaded'),
|
||||
'$id' => ID::custom('sourceChunksUploaded'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
@@ -1137,6 +1483,105 @@ return [
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('screenshotLight'), // File ID from 'screenshots' Console bucket
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 32,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('screenshotDark'), // File ID from 'screenshots' Console bucket
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 32,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildStartAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildEndAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildDuration'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildSize'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('status'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => 'waiting',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildPath'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('buildLogs'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 1000000,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
@@ -1162,16 +1607,23 @@ return [
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_size'),
|
||||
'$id' => ID::custom('_key_sourceSize'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['size'],
|
||||
'attributes' => ['sourceSize'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_buildId'),
|
||||
'$id' => ID::custom('_key_buildSize'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['buildId'],
|
||||
'attributes' => ['buildSize'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_buildDuration'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['buildDuration'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
@@ -1183,167 +1635,17 @@ return [
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_resource_internal_id'),
|
||||
'$id' => ID::custom('_key_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'builds' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('builds'),
|
||||
'name' => 'Builds',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('startTime'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('endTime'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('duration'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('size'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('deploymentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('runtime'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('status'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => 'processing',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('path'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('logs'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 1000000,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('sourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => 'local',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('source'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_deployment'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'attributes' => ['type'],
|
||||
'lengths' => [32],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_deployment_internal_id'),
|
||||
], [
|
||||
'$id' => ID::custom('_key_status'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
'attributes' => ['status'],
|
||||
'lengths' => [16],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -1354,7 +1656,7 @@ return [
|
||||
'name' => 'Executions',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('functionInternalId'),
|
||||
'$id' => ID::custom('resourceInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
@@ -1365,7 +1667,18 @@ return [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('functionId'),
|
||||
'$id' => ID::custom('resourceId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
@@ -1507,17 +1820,6 @@ return [
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('scheduledAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
@@ -1554,19 +1856,12 @@ return [
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_function'),
|
||||
'$id' => ID::custom('_key_resource'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['functionId'],
|
||||
'attributes' => ['resourceInternalId', 'resourceType', 'resourceId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_fulltext_search'),
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_trigger'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
@@ -1616,13 +1911,6 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_function_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['functionInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1686,6 +1974,17 @@ return [
|
||||
'array' => false,
|
||||
'filters' => ['encrypt']
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('secret'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -1741,13 +2040,6 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_resource_internal_id_resource_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ $console = [
|
||||
'hostname' => 'localhost',
|
||||
], // Current host is added on app init
|
||||
],
|
||||
'region' => 'fra',
|
||||
'legalName' => '',
|
||||
'legalCountry' => '',
|
||||
'legalState' => '',
|
||||
|
||||
+42
-6
@@ -375,6 +375,13 @@ return [
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Console */
|
||||
Exception::RESOURCE_ALREADY_EXISTS => [
|
||||
'name' => Exception::RESOURCE_ALREADY_EXISTS,
|
||||
'description' => 'Resource with the requested ID already exists. Please choose a different ID and try again.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Membership */
|
||||
Exception::MEMBERSHIP_NOT_FOUND => [
|
||||
'name' => Exception::MEMBERSHIP_NOT_FOUND,
|
||||
@@ -520,7 +527,7 @@ return [
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::FUNCTION_ENTRYPOINT_MISSING => [
|
||||
'name' => Exception::FUNCTION_RUNTIME_UNSUPPORTED,
|
||||
'name' => Exception::FUNCTION_ENTRYPOINT_MISSING,
|
||||
'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".',
|
||||
'code' => 404,
|
||||
],
|
||||
@@ -534,6 +541,28 @@ return [
|
||||
'description' => 'Function Template with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::FUNCTION_RUNTIME_NOT_DETECTED => [
|
||||
'name' => Exception::FUNCTION_RUNTIME_NOT_DETECTED,
|
||||
'description' => 'Function runtime could not be detected.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Sites */
|
||||
Exception::SITE_NOT_FOUND => [
|
||||
'name' => Exception::SITE_NOT_FOUND,
|
||||
'description' => 'Site with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::SITE_FRAMEWORK_UNSUPPORTED => [
|
||||
'name' => Exception::SITE_FRAMEWORK_UNSUPPORTED,
|
||||
'description' => 'The requested framework is either inactive or unsupported. Please check the value of the _APP_SITES_FRAMEWORKS environment variable.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::SITE_TEMPLATE_NOT_FOUND => [
|
||||
'name' => Exception::SITE_TEMPLATE_NOT_FOUND,
|
||||
'description' => 'Site Template with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Builds */
|
||||
Exception::BUILD_NOT_FOUND => [
|
||||
@@ -577,6 +606,13 @@ return [
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Logs */
|
||||
Exception::LOG_NOT_FOUND => [
|
||||
'name' => Exception::LOG_NOT_FOUND,
|
||||
'description' => 'Log with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Databases */
|
||||
Exception::DATABASE_NOT_FOUND => [
|
||||
'name' => Exception::DATABASE_NOT_FOUND,
|
||||
@@ -593,11 +629,6 @@ return [
|
||||
'description' => 'Database timed out. Try adjusting your queries or adding an index.',
|
||||
'code' => 408
|
||||
],
|
||||
Exception::DATABASE_QUERY_ORDER_NULL => [
|
||||
'name' => Exception::DATABASE_QUERY_ORDER_NULL,
|
||||
'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Collections */
|
||||
Exception::COLLECTION_NOT_FOUND => [
|
||||
@@ -849,6 +880,11 @@ return [
|
||||
'description' => 'Variable with the same ID already exists in this project. Try again with a different ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::VARIABLE_CANNOT_UNSET_SECRET => [
|
||||
'name' => Exception::VARIABLE_CANNOT_UNSET_SECRET,
|
||||
'description' => 'Secret variables cannot be marked as non-secret. Please re-create the variable if this is your intention.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GRAPHQL_NO_QUERY => [
|
||||
'name' => Exception::GRAPHQL_NO_QUERY,
|
||||
'description' => 'Param "query" is not optional.',
|
||||
|
||||
@@ -217,6 +217,34 @@ return [
|
||||
],
|
||||
]
|
||||
],
|
||||
'sites' => [
|
||||
'$model' => Response::MODEL_SITE,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any sites event.',
|
||||
'deployments' => [
|
||||
'$model' => Response::MODEL_DEPLOYMENT,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any deployments event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a deployment is created.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a deployment is deleted.'
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a deployment is updated.'
|
||||
],
|
||||
],
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a site is created.'
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a site is deleted.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a site is updated.',
|
||||
]
|
||||
],
|
||||
'functions' => [
|
||||
'$model' => Response::MODEL_FUNCTION,
|
||||
'$resource' => true,
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* List of Appwrite Sites supported frameworks
|
||||
*/
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
$templateRuntimes = Config::getParam('template-runtimes');
|
||||
|
||||
function getVersions(array $versions, string $prefix)
|
||||
{
|
||||
return array_map(function ($version) use ($prefix) {
|
||||
return $prefix . '-' . $version;
|
||||
}, $versions);
|
||||
}
|
||||
|
||||
return [
|
||||
'analog' => [
|
||||
'key' => 'analog',
|
||||
'name' => 'Analog',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/analog/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/analog/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist/analog',
|
||||
'startCommand' => 'sh helpers/analog/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist/analog/public',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'angular' => [
|
||||
'key' => 'angular',
|
||||
'name' => 'Angular',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/angular/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/angular/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist/angular',
|
||||
'startCommand' => 'sh helpers/angular/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist/angular/browser',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
'fallbackFile' => 'index.csr.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'nextjs' => [
|
||||
'key' => 'nextjs',
|
||||
'name' => 'Next.js',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/next-js/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/next-js/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './.next',
|
||||
'startCommand' => 'sh helpers/next-js/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './out',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
]
|
||||
]
|
||||
],
|
||||
'react' => [
|
||||
'key' => 'react',
|
||||
'name' => 'React',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'nuxt' => [
|
||||
'key' => 'nuxt',
|
||||
'name' => 'Nuxt',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/nuxt/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/nuxt/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './.output',
|
||||
'startCommand' => 'sh helpers/nuxt/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run generate',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './output/public',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
]
|
||||
]
|
||||
],
|
||||
'vue' => [
|
||||
'key' => 'vue',
|
||||
'name' => 'Vue.js',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'sveltekit' => [
|
||||
'key' => 'sveltekit',
|
||||
'name' => 'SvelteKit',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/sveltekit/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/sveltekit/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './build',
|
||||
'startCommand' => 'sh helpers/sveltekit/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './build',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
]
|
||||
]
|
||||
],
|
||||
'astro' => [
|
||||
'key' => 'astro',
|
||||
'name' => 'Astro',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/astro/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/astro/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'sh helpers/astro/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
]
|
||||
]
|
||||
],
|
||||
'remix' => [
|
||||
'key' => 'remix',
|
||||
'name' => 'Remix',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'bundleCommand' => 'sh /usr/local/server/helpers/remix/bundle.sh',
|
||||
'envCommand' => 'source /usr/local/server/helpers/remix/env.sh',
|
||||
'adapters' => [
|
||||
'ssr' => [
|
||||
'key' => 'ssr',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './build',
|
||||
'startCommand' => 'sh helpers/remix/server.sh',
|
||||
],
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './build/client',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
]
|
||||
]
|
||||
],
|
||||
'flutter' => [
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'buildRuntime' => 'flutter-3.29',
|
||||
'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'flutter build web',
|
||||
'installCommand' => '',
|
||||
'outputDirectory' => './build/web',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
],
|
||||
],
|
||||
],
|
||||
'vite' => [
|
||||
'key' => 'vite',
|
||||
'name' => 'Vite',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
],
|
||||
]
|
||||
],
|
||||
'other' => [
|
||||
'key' => 'other',
|
||||
'name' => 'Other',
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'outputDirectory' => './',
|
||||
'startCommand' => 'sh helpers/server.sh',
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
@@ -1,46 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Continent codes with names and approximate central coordinates
|
||||
*
|
||||
* Coordinates represent approximate geographical centers of each continent
|
||||
* Note: These are simplified centroids and may not represent the exact geographical center
|
||||
*/
|
||||
|
||||
return [
|
||||
'AF' => [
|
||||
'name' => 'Africa',
|
||||
'latitude' => 8.7832,
|
||||
'longitude' => 34.5085
|
||||
],
|
||||
'AN' => [
|
||||
'name' => 'Antarctica',
|
||||
'latitude' => -82.8628,
|
||||
'longitude' => 135.0000
|
||||
],
|
||||
'AS' => [
|
||||
'name' => 'Asia',
|
||||
'latitude' => 34.0479,
|
||||
'longitude' => 100.6197
|
||||
],
|
||||
'EU' => [
|
||||
'name' => 'Europe',
|
||||
'latitude' => 54.5260,
|
||||
'longitude' => 15.2551
|
||||
],
|
||||
'NA' => [
|
||||
'name' => 'North America',
|
||||
'latitude' => 54.5260,
|
||||
'longitude' => -105.2551
|
||||
],
|
||||
'OC' => [
|
||||
'name' => 'Oceania',
|
||||
'latitude' => -22.7359,
|
||||
'longitude' => 140.0188
|
||||
],
|
||||
'SA' => [
|
||||
'name' => 'South America',
|
||||
'latitude' => -8.7832,
|
||||
'longitude' => -55.4915
|
||||
],
|
||||
'AF',
|
||||
'AN',
|
||||
'AS',
|
||||
'EU',
|
||||
'NA',
|
||||
'OC',
|
||||
'SA',
|
||||
];
|
||||
|
||||
+198
-199
@@ -1,210 +1,209 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ISO 3166 standard country codes with country names and coordinates
|
||||
* ISO 3166 standard country codes
|
||||
* https://www.iso.org/iso-3166-country-codes.html
|
||||
*
|
||||
* Source:
|
||||
* https://www.iso.org/obp/ui/#search/code/
|
||||
* Coordinates source: Natural Earth Data (approximate country centroids)
|
||||
*/
|
||||
|
||||
return [
|
||||
'AF' => ['name' => 'Afghanistan', 'latitude' => 33.0, 'longitude' => 66.0],
|
||||
'AO' => ['name' => 'Angola', 'latitude' => -12.5, 'longitude' => 18.5],
|
||||
'AL' => ['name' => 'Albania', 'latitude' => 41.0, 'longitude' => 20.0],
|
||||
'AD' => ['name' => 'Andorra', 'latitude' => 42.5, 'longitude' => 1.6],
|
||||
'AE' => ['name' => 'United Arab Emirates', 'latitude' => 24.0, 'longitude' => 54.0],
|
||||
'AR' => ['name' => 'Argentina', 'latitude' => -34.0, 'longitude' => -64.0],
|
||||
'AM' => ['name' => 'Armenia', 'latitude' => 40.0, 'longitude' => 45.0],
|
||||
'AG' => ['name' => 'Antigua and Barbuda', 'latitude' => 17.05, 'longitude' => -61.8],
|
||||
'AU' => ['name' => 'Australia', 'latitude' => -25.0, 'longitude' => 135.0],
|
||||
'AT' => ['name' => 'Austria', 'latitude' => 47.3, 'longitude' => 13.3],
|
||||
'AZ' => ['name' => 'Azerbaijan', 'latitude' => 40.5, 'longitude' => 47.5],
|
||||
'BI' => ['name' => 'Burundi', 'latitude' => -3.5, 'longitude' => 30.0],
|
||||
'BE' => ['name' => 'Belgium', 'latitude' => 50.8, 'longitude' => 4.0],
|
||||
'BJ' => ['name' => 'Benin', 'latitude' => 9.5, 'longitude' => 2.25],
|
||||
'BF' => ['name' => 'Burkina Faso', 'latitude' => 13.0, 'longitude' => -2.0],
|
||||
'BD' => ['name' => 'Bangladesh', 'latitude' => 24.0, 'longitude' => 90.0],
|
||||
'BG' => ['name' => 'Bulgaria', 'latitude' => 43.0, 'longitude' => 25.0],
|
||||
'BH' => ['name' => 'Bahrain', 'latitude' => 26.0, 'longitude' => 50.5],
|
||||
'BS' => ['name' => 'Bahamas', 'latitude' => 24.25, 'longitude' => -76.0],
|
||||
'BA' => ['name' => 'Bosnia and Herzegovina', 'latitude' => 44.0, 'longitude' => 18.0],
|
||||
'BY' => ['name' => 'Belarus', 'latitude' => 53.0, 'longitude' => 28.0],
|
||||
'BZ' => ['name' => 'Belize', 'latitude' => 17.25, 'longitude' => -88.75],
|
||||
'BO' => ['name' => 'Bolivia', 'latitude' => -17.0, 'longitude' => -65.0],
|
||||
'BR' => ['name' => 'Brazil', 'latitude' => -10.0, 'longitude' => -55.0],
|
||||
'BB' => ['name' => 'Barbados', 'latitude' => 13.17, 'longitude' => -59.53],
|
||||
'BN' => ['name' => 'Brunei', 'latitude' => 4.5, 'longitude' => 114.67],
|
||||
'BT' => ['name' => 'Bhutan', 'latitude' => 27.5, 'longitude' => 90.5],
|
||||
'BW' => ['name' => 'Botswana', 'latitude' => -22.0, 'longitude' => 24.0],
|
||||
'CF' => ['name' => 'Central African Republic', 'latitude' => 7.0, 'longitude' => 21.0],
|
||||
'CA' => ['name' => 'Canada', 'latitude' => 60.0, 'longitude' => -95.0],
|
||||
'CH' => ['name' => 'Switzerland', 'latitude' => 47.0, 'longitude' => 8.0],
|
||||
'CL' => ['name' => 'Chile', 'latitude' => -30.0, 'longitude' => -71.0],
|
||||
'CN' => ['name' => 'China', 'latitude' => 35.0, 'longitude' => 105.0],
|
||||
'CI' => ['name' => 'Côte d\'Ivoire', 'latitude' => 8.0, 'longitude' => -5.0],
|
||||
'CM' => ['name' => 'Cameroon', 'latitude' => 6.0, 'longitude' => 12.0],
|
||||
'CD' => ['name' => 'Democratic Republic of the Congo', 'latitude' => -2.5, 'longitude' => 23.5],
|
||||
'CG' => ['name' => 'Republic of the Congo', 'latitude' => -1.0, 'longitude' => 15.0],
|
||||
'CO' => ['name' => 'Colombia', 'latitude' => 4.0, 'longitude' => -72.0],
|
||||
'KM' => ['name' => 'Comoros', 'latitude' => -12.17, 'longitude' => 44.25],
|
||||
'CV' => ['name' => 'Cape Verde', 'latitude' => 16.0, 'longitude' => -24.0],
|
||||
'CR' => ['name' => 'Costa Rica', 'latitude' => 10.0, 'longitude' => -84.0],
|
||||
'CU' => ['name' => 'Cuba', 'latitude' => 21.5, 'longitude' => -80.0],
|
||||
'CY' => ['name' => 'Cyprus', 'latitude' => 35.0, 'longitude' => 33.0],
|
||||
'CZ' => ['name' => 'Czech Republic', 'latitude' => 49.75, 'longitude' => 15.5],
|
||||
'DE' => ['name' => 'Germany', 'latitude' => 51.0, 'longitude' => 9.0],
|
||||
'DJ' => ['name' => 'Djibouti', 'latitude' => 11.5, 'longitude' => 43.0],
|
||||
'DM' => ['name' => 'Dominica', 'latitude' => 15.42, 'longitude' => -61.33],
|
||||
'DK' => ['name' => 'Denmark', 'latitude' => 56.0, 'longitude' => 10.0],
|
||||
'DO' => ['name' => 'Dominican Republic', 'latitude' => 19.0, 'longitude' => -70.67],
|
||||
'DZ' => ['name' => 'Algeria', 'latitude' => 28.0, 'longitude' => 3.0],
|
||||
'EC' => ['name' => 'Ecuador', 'latitude' => -2.0, 'longitude' => -77.5],
|
||||
'EG' => ['name' => 'Egypt', 'latitude' => 27.0, 'longitude' => 30.0],
|
||||
'ER' => ['name' => 'Eritrea', 'latitude' => 15.0, 'longitude' => 39.0],
|
||||
'ES' => ['name' => 'Spain', 'latitude' => 40.0, 'longitude' => -4.0],
|
||||
'EE' => ['name' => 'Estonia', 'latitude' => 59.0, 'longitude' => 26.0],
|
||||
'ET' => ['name' => 'Ethiopia', 'latitude' => 8.0, 'longitude' => 38.0],
|
||||
'FI' => ['name' => 'Finland', 'latitude' => 64.0, 'longitude' => 26.0],
|
||||
'FJ' => ['name' => 'Fiji', 'latitude' => -18.0, 'longitude' => 175.0],
|
||||
'FR' => ['name' => 'France', 'latitude' => 46.0, 'longitude' => 2.0],
|
||||
'FM' => ['name' => 'Micronesia', 'latitude' => 6.92, 'longitude' => 158.25],
|
||||
'GA' => ['name' => 'Gabon', 'latitude' => -1.0, 'longitude' => 11.75],
|
||||
'GB' => ['name' => 'United Kingdom', 'latitude' => 54.0, 'longitude' => -2.0],
|
||||
'GE' => ['name' => 'Georgia', 'latitude' => 42.0, 'longitude' => 43.5],
|
||||
'GH' => ['name' => 'Ghana', 'latitude' => 8.0, 'longitude' => -2.0],
|
||||
'GN' => ['name' => 'Guinea', 'latitude' => 11.0, 'longitude' => -10.0],
|
||||
'GM' => ['name' => 'Gambia', 'latitude' => 13.47, 'longitude' => -16.57],
|
||||
'GW' => ['name' => 'Guinea-Bissau', 'latitude' => 12.0, 'longitude' => -15.0],
|
||||
'GQ' => ['name' => 'Equatorial Guinea', 'latitude' => 2.0, 'longitude' => 10.0],
|
||||
'GR' => ['name' => 'Greece', 'latitude' => 39.0, 'longitude' => 22.0],
|
||||
'GD' => ['name' => 'Grenada', 'latitude' => 12.12, 'longitude' => -61.67],
|
||||
'GT' => ['name' => 'Guatemala', 'latitude' => 15.5, 'longitude' => -90.25],
|
||||
'GY' => ['name' => 'Guyana', 'latitude' => 5.0, 'longitude' => -59.0],
|
||||
'HK' => ['name' => 'Hong Kong', 'latitude' => 22.25, 'longitude' => 114.17],
|
||||
'HN' => ['name' => 'Honduras', 'latitude' => 15.0, 'longitude' => -86.5],
|
||||
'HR' => ['name' => 'Croatia', 'latitude' => 45.17, 'longitude' => 15.5],
|
||||
'HT' => ['name' => 'Haiti', 'latitude' => 19.0, 'longitude' => -72.42],
|
||||
'HU' => ['name' => 'Hungary', 'latitude' => 47.0, 'longitude' => 20.0],
|
||||
'ID' => ['name' => 'Indonesia', 'latitude' => -5.0, 'longitude' => 120.0],
|
||||
'IN' => ['name' => 'India', 'latitude' => 20.0, 'longitude' => 77.0],
|
||||
'IE' => ['name' => 'Ireland', 'latitude' => 53.0, 'longitude' => -8.0],
|
||||
'IR' => ['name' => 'Iran', 'latitude' => 32.0, 'longitude' => 53.0],
|
||||
'IQ' => ['name' => 'Iraq', 'latitude' => 33.0, 'longitude' => 44.0],
|
||||
'IS' => ['name' => 'Iceland', 'latitude' => 65.0, 'longitude' => -18.0],
|
||||
'IL' => ['name' => 'Israel', 'latitude' => 31.5, 'longitude' => 34.75],
|
||||
'IT' => ['name' => 'Italy', 'latitude' => 42.83, 'longitude' => 12.83],
|
||||
'JM' => ['name' => 'Jamaica', 'latitude' => 18.25, 'longitude' => -77.5],
|
||||
'JO' => ['name' => 'Jordan', 'latitude' => 31.0, 'longitude' => 36.0],
|
||||
'JP' => ['name' => 'Japan', 'latitude' => 36.0, 'longitude' => 138.0],
|
||||
'KZ' => ['name' => 'Kazakhstan', 'latitude' => 48.0, 'longitude' => 68.0],
|
||||
'KE' => ['name' => 'Kenya', 'latitude' => 1.0, 'longitude' => 38.0],
|
||||
'KG' => ['name' => 'Kyrgyzstan', 'latitude' => 41.0, 'longitude' => 75.0],
|
||||
'KH' => ['name' => 'Cambodia', 'latitude' => 13.0, 'longitude' => 105.0],
|
||||
'KI' => ['name' => 'Kiribati', 'latitude' => 1.42, 'longitude' => 173.0],
|
||||
'KN' => ['name' => 'Saint Kitts and Nevis', 'latitude' => 17.33, 'longitude' => -62.75],
|
||||
'KR' => ['name' => 'South Korea', 'latitude' => 37.0, 'longitude' => 127.5],
|
||||
'KW' => ['name' => 'Kuwait', 'latitude' => 29.34, 'longitude' => 47.66],
|
||||
'LA' => ['name' => 'Laos', 'latitude' => 18.0, 'longitude' => 105.0],
|
||||
'LB' => ['name' => 'Lebanon', 'latitude' => 33.83, 'longitude' => 35.83],
|
||||
'LR' => ['name' => 'Liberia', 'latitude' => 6.5, 'longitude' => -9.5],
|
||||
'LY' => ['name' => 'Libya', 'latitude' => 25.0, 'longitude' => 17.0],
|
||||
'LC' => ['name' => 'Saint Lucia', 'latitude' => 13.88, 'longitude' => -61.13],
|
||||
'LI' => ['name' => 'Liechtenstein', 'latitude' => 47.17, 'longitude' => 9.53],
|
||||
'LK' => ['name' => 'Sri Lanka', 'latitude' => 7.0, 'longitude' => 81.0],
|
||||
'LS' => ['name' => 'Lesotho', 'latitude' => -29.5, 'longitude' => 28.5],
|
||||
'LT' => ['name' => 'Lithuania', 'latitude' => 56.0, 'longitude' => 24.0],
|
||||
'LU' => ['name' => 'Luxembourg', 'latitude' => 49.75, 'longitude' => 6.17],
|
||||
'LV' => ['name' => 'latitudevia', 'latitude' => 57.0, 'longitude' => 25.0],
|
||||
'MA' => ['name' => 'Morocco', 'latitude' => 32.0, 'longitude' => -5.0],
|
||||
'MC' => ['name' => 'Monaco', 'latitude' => 43.73, 'longitude' => 7.4],
|
||||
'MD' => ['name' => 'Moldova', 'latitude' => 47.0, 'longitude' => 29.0],
|
||||
'MG' => ['name' => 'Madagascar', 'latitude' => -20.0, 'longitude' => 47.0],
|
||||
'MV' => ['name' => 'Maldives', 'latitude' => 3.25, 'longitude' => 73.0],
|
||||
'MX' => ['name' => 'Mexico', 'latitude' => 23.0, 'longitude' => -102.0],
|
||||
'MH' => ['name' => 'Marshall Islands', 'latitude' => 9.0, 'longitude' => 168.0],
|
||||
'MK' => ['name' => 'North Macedonia', 'latitude' => 41.83, 'longitude' => 22.0],
|
||||
'ML' => ['name' => 'Mali', 'latitude' => 17.0, 'longitude' => -4.0],
|
||||
'MT' => ['name' => 'Malta', 'latitude' => 35.83, 'longitude' => 14.58],
|
||||
'MM' => ['name' => 'Myanmar', 'latitude' => 22.0, 'longitude' => 98.0],
|
||||
'ME' => ['name' => 'Montenegro', 'latitude' => 42.5, 'longitude' => 19.3],
|
||||
'MN' => ['name' => 'Mongolia', 'latitude' => 46.0, 'longitude' => 105.0],
|
||||
'MZ' => ['name' => 'Mozambique', 'latitude' => -18.25, 'longitude' => 35.0],
|
||||
'MR' => ['name' => 'Mauritania', 'latitude' => 20.0, 'longitude' => -12.0],
|
||||
'MU' => ['name' => 'Mauritius', 'latitude' => -20.28, 'longitude' => 57.55],
|
||||
'MW' => ['name' => 'Malawi', 'latitude' => -13.5, 'longitude' => 34.0],
|
||||
'MY' => ['name' => 'Malaysia', 'latitude' => 2.5, 'longitude' => 112.5],
|
||||
'NA' => ['name' => 'Namibia', 'latitude' => -22.0, 'longitude' => 17.0],
|
||||
'NE' => ['name' => 'Niger', 'latitude' => 16.0, 'longitude' => 8.0],
|
||||
'NG' => ['name' => 'Nigeria', 'latitude' => 10.0, 'longitude' => 8.0],
|
||||
'NI' => ['name' => 'Nicaragua', 'latitude' => 13.0, 'longitude' => -85.0],
|
||||
'NL' => ['name' => 'Netherlands', 'latitude' => 52.5, 'longitude' => 5.75],
|
||||
'NO' => ['name' => 'Norway', 'latitude' => 62.0, 'longitude' => 10.0],
|
||||
'NP' => ['name' => 'Nepal', 'latitude' => 28.0, 'longitude' => 84.0],
|
||||
'NR' => ['name' => 'Nauru', 'latitude' => -0.53, 'longitude' => 166.92],
|
||||
'NZ' => ['name' => 'New Zealand', 'latitude' => -41.0, 'longitude' => 174.0],
|
||||
'OM' => ['name' => 'Oman', 'latitude' => 21.0, 'longitude' => 57.0],
|
||||
'PK' => ['name' => 'Pakistan', 'latitude' => 30.0, 'longitude' => 70.0],
|
||||
'PS' => ['name' => 'Palestine', 'latitude' => 31.9, 'longitude' => 35.2],
|
||||
'PA' => ['name' => 'Panama', 'latitude' => 9.0, 'longitude' => -80.0],
|
||||
'PE' => ['name' => 'Peru', 'latitude' => -10.0, 'longitude' => -76.0],
|
||||
'PH' => ['name' => 'Philippines', 'latitude' => 13.0, 'longitude' => 122.0],
|
||||
'PW' => ['name' => 'Palau', 'latitude' => 7.5, 'longitude' => 134.5],
|
||||
'PG' => ['name' => 'Papua New Guinea', 'latitude' => -6.0, 'longitude' => 147.0],
|
||||
'PL' => ['name' => 'Poland', 'latitude' => 52.0, 'longitude' => 20.0],
|
||||
'KP' => ['name' => 'North Korea', 'latitude' => 40.0, 'longitude' => 127.0],
|
||||
'PT' => ['name' => 'Portugal', 'latitude' => 39.5, 'longitude' => -8.0],
|
||||
'PY' => ['name' => 'Paraguay', 'latitude' => -23.0, 'longitude' => -58.0],
|
||||
'QA' => ['name' => 'Qatar', 'latitude' => 25.5, 'longitude' => 51.25],
|
||||
'RO' => ['name' => 'Romania', 'latitude' => 46.0, 'longitude' => 25.0],
|
||||
'RU' => ['name' => 'Russia', 'latitude' => 60.0, 'longitude' => 100.0],
|
||||
'RW' => ['name' => 'Rwanda', 'latitude' => -2.0, 'longitude' => 30.0],
|
||||
'SA' => ['name' => 'Saudi Arabia', 'latitude' => 25.0, 'longitude' => 45.0],
|
||||
'SD' => ['name' => 'Sudan', 'latitude' => 15.0, 'longitude' => 30.0],
|
||||
'SN' => ['name' => 'Senegal', 'latitude' => 14.0, 'longitude' => -14.0],
|
||||
'SG' => ['name' => 'Singapore', 'latitude' => 1.37, 'longitude' => 103.8],
|
||||
'SB' => ['name' => 'Solomon Islands', 'latitude' => -8.0, 'longitude' => 159.0],
|
||||
'SL' => ['name' => 'Sierra Leone', 'latitude' => 8.5, 'longitude' => -11.5],
|
||||
'SV' => ['name' => 'El Salvador', 'latitude' => 13.83, 'longitude' => -88.92],
|
||||
'SM' => ['name' => 'San Marino', 'latitude' => 43.77, 'longitude' => 12.42],
|
||||
'SO' => ['name' => 'Somalia', 'latitude' => 10.0, 'longitude' => 49.0],
|
||||
'RS' => ['name' => 'Serbia', 'latitude' => 44.0, 'longitude' => 21.0],
|
||||
'SS' => ['name' => 'South Sudan', 'latitude' => 8.0, 'longitude' => 30.0],
|
||||
'ST' => ['name' => 'São Tomé and Príncipe', 'latitude' => 1.0, 'longitude' => 7.0],
|
||||
'SR' => ['name' => 'Suriname', 'latitude' => 4.0, 'longitude' => -56.0],
|
||||
'SK' => ['name' => 'Slovakia', 'latitude' => 48.67, 'longitude' => 19.5],
|
||||
'SI' => ['name' => 'Slovenia', 'latitude' => 46.0, 'longitude' => 15.0],
|
||||
'SE' => ['name' => 'Sweden', 'latitude' => 62.0, 'longitude' => 15.0],
|
||||
'SZ' => ['name' => 'Eswatini', 'latitude' => -26.5, 'longitude' => 31.5],
|
||||
'SC' => ['name' => 'Seychelles', 'latitude' => -4.58, 'longitude' => 55.67],
|
||||
'SY' => ['name' => 'Syria', 'latitude' => 35.0, 'longitude' => 38.0],
|
||||
'TD' => ['name' => 'Chad', 'latitude' => 15.0, 'longitude' => 19.0],
|
||||
'TG' => ['name' => 'Togo', 'latitude' => 8.0, 'longitude' => 1.17],
|
||||
'TH' => ['name' => 'Thailand', 'latitude' => 15.0, 'longitude' => 100.0],
|
||||
'TJ' => ['name' => 'Tajikistan', 'latitude' => 39.0, 'longitude' => 71.0],
|
||||
'TM' => ['name' => 'Turkmenistan', 'latitude' => 40.0, 'longitude' => 60.0],
|
||||
'TL' => ['name' => 'Timor-Leste', 'latitude' => -8.83, 'longitude' => 125.92],
|
||||
'TO' => ['name' => 'Tonga', 'latitude' => -20.0, 'longitude' => -175.0],
|
||||
'TT' => ['name' => 'Trinidad and Tobago', 'latitude' => 11.0, 'longitude' => -61.0],
|
||||
'TN' => ['name' => 'Tunisia', 'latitude' => 34.0, 'longitude' => 9.0],
|
||||
'TR' => ['name' => 'Turkey', 'latitude' => 39.0, 'longitude' => 35.0],
|
||||
'TV' => ['name' => 'Tuvalu', 'latitude' => -8.0, 'longitude' => 178.0],
|
||||
'TZ' => ['name' => 'Tanzania', 'latitude' => -6.0, 'longitude' => 35.0],
|
||||
'TW' => ['name' => 'Taiwan', 'latitude' => 23.5, 'longitude' => 121.0],
|
||||
'UG' => ['name' => 'Uganda', 'latitude' => 1.0, 'longitude' => 32.0],
|
||||
'UA' => ['name' => 'Ukraine', 'latitude' => 49.0, 'longitude' => 32.0],
|
||||
'UY' => ['name' => 'Uruguay', 'latitude' => -33.0, 'longitude' => -56.0],
|
||||
'US' => ['name' => 'United States', 'latitude' => 38.0, 'longitude' => -97.0],
|
||||
'UZ' => ['name' => 'Uzbekistan', 'latitude' => 41.0, 'longitude' => 64.0],
|
||||
'VA' => ['name' => 'Vatican City', 'latitude' => 41.9, 'longitude' => 12.45],
|
||||
'VC' => ['name' => 'Saint Vincent and the Grenadines', 'latitude' => 13.25, 'longitude' => -61.2],
|
||||
'VE' => ['name' => 'Venezuela', 'latitude' => 8.0, 'longitude' => -66.0],
|
||||
'VN' => ['name' => 'Vietnam', 'latitude' => 16.0, 'longitude' => 106.0],
|
||||
'VU' => ['name' => 'Vanuatu', 'latitude' => -16.0, 'longitude' => 167.0],
|
||||
'WS' => ['name' => 'Samoa', 'latitude' => -13.58, 'longitude' => -172.33],
|
||||
'YE' => ['name' => 'Yemen', 'latitude' => 15.0, 'longitude' => 48.0],
|
||||
'ZA' => ['name' => 'South Africa', 'latitude' => -29.0, 'longitude' => 24.0],
|
||||
'ZM' => ['name' => 'Zambia', 'latitude' => -15.0, 'longitude' => 30.0],
|
||||
'ZW' => ['name' => 'Zimbabwe', 'latitude' => -20.0, 'longitude' => 30.0],
|
||||
'AF',
|
||||
'AO',
|
||||
'AL',
|
||||
'AD',
|
||||
'AE',
|
||||
'AR',
|
||||
'AM',
|
||||
'AG',
|
||||
'AU',
|
||||
'AT',
|
||||
'AZ',
|
||||
'BI',
|
||||
'BE',
|
||||
'BJ',
|
||||
'BF',
|
||||
'BD',
|
||||
'BG',
|
||||
'BH',
|
||||
'BS',
|
||||
'BA',
|
||||
'BY',
|
||||
'BZ',
|
||||
'BO',
|
||||
'BR',
|
||||
'BB',
|
||||
'BN',
|
||||
'BT',
|
||||
'BW',
|
||||
'CF',
|
||||
'CA',
|
||||
'CH',
|
||||
'CL',
|
||||
'CN',
|
||||
'CI',
|
||||
'CM',
|
||||
'CD',
|
||||
'CG',
|
||||
'CO',
|
||||
'KM',
|
||||
'CV',
|
||||
'CR',
|
||||
'CU',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DE',
|
||||
'DJ',
|
||||
'DM',
|
||||
'DK',
|
||||
'DO',
|
||||
'DZ',
|
||||
'EC',
|
||||
'EG',
|
||||
'ER',
|
||||
'ES',
|
||||
'EE',
|
||||
'ET',
|
||||
'FI',
|
||||
'FJ',
|
||||
'FR',
|
||||
'FM',
|
||||
'GA',
|
||||
'GB',
|
||||
'GE',
|
||||
'GH',
|
||||
'GN',
|
||||
'GM',
|
||||
'GW',
|
||||
'GQ',
|
||||
'GR',
|
||||
'GD',
|
||||
'GT',
|
||||
'GY',
|
||||
'HK',
|
||||
'HN',
|
||||
'HR',
|
||||
'HT',
|
||||
'HU',
|
||||
'ID',
|
||||
'IN',
|
||||
'IE',
|
||||
'IR',
|
||||
'IQ',
|
||||
'IS',
|
||||
'IL',
|
||||
'IT',
|
||||
'JM',
|
||||
'JO',
|
||||
'JP',
|
||||
'KZ',
|
||||
'KE',
|
||||
'KG',
|
||||
'KH',
|
||||
'KI',
|
||||
'KN',
|
||||
'KR',
|
||||
'KW',
|
||||
'LA',
|
||||
'LB',
|
||||
'LR',
|
||||
'LY',
|
||||
'LC',
|
||||
'LI',
|
||||
'LK',
|
||||
'LS',
|
||||
'LT',
|
||||
'LU',
|
||||
'LV',
|
||||
'MA',
|
||||
'MC',
|
||||
'MD',
|
||||
'MG',
|
||||
'MV',
|
||||
'MX',
|
||||
'MH',
|
||||
'MK',
|
||||
'ML',
|
||||
'MT',
|
||||
'MM',
|
||||
'ME',
|
||||
'MN',
|
||||
'MZ',
|
||||
'MR',
|
||||
'MU',
|
||||
'MW',
|
||||
'MY',
|
||||
'NA',
|
||||
'NE',
|
||||
'NG',
|
||||
'NI',
|
||||
'NL',
|
||||
'NO',
|
||||
'NP',
|
||||
'NR',
|
||||
'NZ',
|
||||
'OM',
|
||||
'PK',
|
||||
'PS',
|
||||
'PA',
|
||||
'PE',
|
||||
'PH',
|
||||
'PW',
|
||||
'PG',
|
||||
'PL',
|
||||
'KP',
|
||||
'PT',
|
||||
'PY',
|
||||
'QA',
|
||||
'RO',
|
||||
'RU',
|
||||
'RW',
|
||||
'SA',
|
||||
'SD',
|
||||
'SN',
|
||||
'SG',
|
||||
'SB',
|
||||
'SL',
|
||||
'SV',
|
||||
'SM',
|
||||
'SO',
|
||||
'RS',
|
||||
'SS',
|
||||
'ST',
|
||||
'SR',
|
||||
'SK',
|
||||
'SI',
|
||||
'SE',
|
||||
'SZ',
|
||||
'SC',
|
||||
'SY',
|
||||
'TD',
|
||||
'TG',
|
||||
'TH',
|
||||
'TJ',
|
||||
'TM',
|
||||
'TL',
|
||||
'TO',
|
||||
'TT',
|
||||
'TN',
|
||||
'TR',
|
||||
'TV',
|
||||
'TZ',
|
||||
'TW',
|
||||
'UG',
|
||||
'UA',
|
||||
'UY',
|
||||
'US',
|
||||
'UZ',
|
||||
'VA',
|
||||
'VC',
|
||||
'VE',
|
||||
'VN',
|
||||
'VU',
|
||||
'WS',
|
||||
'YE',
|
||||
'ZA',
|
||||
'ZM',
|
||||
'ZW',
|
||||
];
|
||||
|
||||
@@ -142,16 +142,6 @@ return [
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'figma' => [
|
||||
'name' => 'Figma',
|
||||
'developers' => 'https://www.figma.com/developers/api#oauth2',
|
||||
'icon' => 'icon-figma',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'github' => [
|
||||
'name' => 'GitHub',
|
||||
'developers' => 'https://developer.github.com/',
|
||||
|
||||
+16
-16
@@ -11,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '17.0.2',
|
||||
'version' => '16.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -59,7 +59,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '15.0.2',
|
||||
'version' => '13.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -77,7 +77,7 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '9.0.1',
|
||||
'version' => '7.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -112,7 +112,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '7.0.1',
|
||||
'version' => '6.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -134,7 +134,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.7.3',
|
||||
'version' => '0.6.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -217,7 +217,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '6.2.3',
|
||||
'version' => '6.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -245,7 +245,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '16.1.0-rc.1',
|
||||
'version' => '14.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -263,7 +263,7 @@ return [
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '14.1.0-rc.1',
|
||||
'version' => '12.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -281,7 +281,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '14.1.0-rc.1',
|
||||
'version' => '12.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -299,7 +299,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '10.1.0-rc.1',
|
||||
'version' => '6.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -317,7 +317,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '15.1.0-rc.1',
|
||||
'version' => '12.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -335,7 +335,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.6.0-rc.1',
|
||||
'version' => '0.3.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -353,7 +353,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.13.0-rc.1',
|
||||
'version' => '0.11.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -371,7 +371,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '15.1.0-rc.1',
|
||||
'version' => '12.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -389,7 +389,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '8.1.0-rc.1',
|
||||
'version' => '6.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -411,7 +411,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '9.1.0-rc.1',
|
||||
'version' => '6.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
+65
-1
@@ -3,8 +3,72 @@
|
||||
return [
|
||||
'default' => [
|
||||
'$id' => 'default',
|
||||
'name' => 'default',
|
||||
'name' => 'Frankfurt',
|
||||
'disabled' => false,
|
||||
'flag' => 'de',
|
||||
'default' => true,
|
||||
],
|
||||
'fra' => [
|
||||
'$id' => 'fra',
|
||||
'name' => 'Frankfurt',
|
||||
'disabled' => false,
|
||||
'flag' => 'de',
|
||||
'default' => true,
|
||||
],
|
||||
'nyc' => [
|
||||
'$id' => 'nyc',
|
||||
'name' => 'New York',
|
||||
'disabled' => true,
|
||||
'flag' => 'us',
|
||||
'default' => true,
|
||||
],
|
||||
'sfo' => [
|
||||
'$id' => 'sfo',
|
||||
'name' => 'San Francisco',
|
||||
'disabled' => true,
|
||||
'flag' => 'us',
|
||||
'default' => true,
|
||||
],
|
||||
'blr' => [
|
||||
'$id' => 'blr',
|
||||
'name' => 'Bengaluru',
|
||||
'disabled' => true,
|
||||
'flag' => 'in',
|
||||
'default' => true,
|
||||
],
|
||||
'lon' => [
|
||||
'$id' => 'lon',
|
||||
'name' => 'London',
|
||||
'disabled' => true,
|
||||
'flag' => 'gb',
|
||||
'default' => true,
|
||||
],
|
||||
'ams' => [
|
||||
'$id' => 'ams',
|
||||
'name' => 'Amsterdam',
|
||||
'disabled' => true,
|
||||
'flag' => 'nl',
|
||||
'default' => true,
|
||||
],
|
||||
'sgp' => [
|
||||
'$id' => 'sgp',
|
||||
'name' => 'Singapore',
|
||||
'disabled' => true,
|
||||
'flag' => 'sg',
|
||||
'default' => true,
|
||||
],
|
||||
'tor' => [
|
||||
'$id' => 'tor',
|
||||
'name' => 'Toronto',
|
||||
'disabled' => true,
|
||||
'flag' => 'ca',
|
||||
'default' => true,
|
||||
],
|
||||
'syd' => [
|
||||
'$id' => 'syd',
|
||||
'name' => 'Sydney',
|
||||
'disabled' => true,
|
||||
'flag' => 'au',
|
||||
'default' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -26,6 +26,7 @@ $member = [
|
||||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'assistant.read',
|
||||
'rules.read'
|
||||
];
|
||||
|
||||
$admins = [
|
||||
@@ -58,6 +59,10 @@ $admins = [
|
||||
'health.read',
|
||||
'functions.read',
|
||||
'functions.write',
|
||||
'sites.read',
|
||||
'sites.write',
|
||||
'log.read',
|
||||
'log.write',
|
||||
'execution.read',
|
||||
'execution.write',
|
||||
'rules.read',
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
return (new Runtimes('v4'))->getAll();
|
||||
return (new Runtimes('v5'))->getAll();
|
||||
|
||||
@@ -64,6 +64,18 @@ return [ // List of publicly visible scopes
|
||||
'functions.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s functions and code deployments',
|
||||
],
|
||||
'sites.read' => [
|
||||
'description' => 'Access to read your project\'s sites and deployments',
|
||||
],
|
||||
'sites.write' => [
|
||||
'description' => 'Access to create, update, and delete your project\'s sites and deployments',
|
||||
],
|
||||
'log.read' => [
|
||||
'description' => 'Access to read your site\'s logs',
|
||||
],
|
||||
'log.write' => [
|
||||
'description' => 'Access to update, and delete your site\'s logs',
|
||||
],
|
||||
'execution.read' => [
|
||||
'description' => 'Access to read your project\'s execution logs',
|
||||
],
|
||||
|
||||
+17
-1
@@ -65,6 +65,9 @@ return [
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/databases.png',
|
||||
'globalAttributes' => [
|
||||
'databaseId'
|
||||
]
|
||||
],
|
||||
'locale' => [
|
||||
'key' => 'locale',
|
||||
@@ -170,12 +173,25 @@ return [
|
||||
'optional' => false,
|
||||
'icon' => '',
|
||||
],
|
||||
'sites' => [
|
||||
'key' => 'sites',
|
||||
'name' => 'Sites',
|
||||
'subtitle' => 'The Sites Service allows you view, create and manage your web applications.',
|
||||
'description' => '/docs/services/sites.md',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => 'https://appwrite.io/docs/sites',
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '', // TODO: Update icon later
|
||||
],
|
||||
'functions' => [
|
||||
'key' => 'functions',
|
||||
'name' => 'Functions',
|
||||
'subtitle' => 'The Functions Service allows you view, create and manage your Cloud Functions.',
|
||||
'description' => '/docs/services/functions.md',
|
||||
'controller' => 'api/functions.php',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => 'https://appwrite.io/docs/functions',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Functions\Specification;
|
||||
use Appwrite\Platform\Modules\Compute\Specification;
|
||||
|
||||
return [
|
||||
Specification::S_05VCPU_512MB => [
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'NODE' => [
|
||||
'name' => 'node',
|
||||
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
],
|
||||
'PYTHON' => [
|
||||
'name' => 'python',
|
||||
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
'versions' => ['1.23']
|
||||
],
|
||||
'PHP' => [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'DENO' => [
|
||||
'name' => 'deno',
|
||||
'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21']
|
||||
],
|
||||
'BUN' => [
|
||||
'name' => 'bun',
|
||||
'versions' => ['1.1', '1.0']
|
||||
],
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
'versions' => ['3.3', '3.2', '3.1', '3.0']
|
||||
],
|
||||
'FLUTTER' => [
|
||||
'name' => 'flutter',
|
||||
'versions' => ['3.24']
|
||||
],
|
||||
];
|
||||
@@ -1,39 +1,8 @@
|
||||
<?php
|
||||
|
||||
const TEMPLATE_RUNTIMES = [
|
||||
'NODE' => [
|
||||
'name' => 'node',
|
||||
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
],
|
||||
'PYTHON' => [
|
||||
'name' => 'python',
|
||||
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
'versions' => ['1.23']
|
||||
],
|
||||
'PHP' => [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'DENO' => [
|
||||
'name' => 'deno',
|
||||
'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21']
|
||||
],
|
||||
'BUN' => [
|
||||
'name' => 'bun',
|
||||
'versions' => ['1.1', '1.0']
|
||||
],
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
'versions' => ['3.3', '3.2', '3.1', '3.0']
|
||||
],
|
||||
];
|
||||
use Utopia\Config\Config;
|
||||
|
||||
$templateRuntimes = Config::getParam('template-runtimes');
|
||||
|
||||
function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = [])
|
||||
{
|
||||
@@ -62,24 +31,24 @@ return [
|
||||
'timeout' => 15,
|
||||
'useCases' => ['starter'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/starter'),
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter'),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['GO'], '', 'main.go', 'go/starter'),
|
||||
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
|
||||
...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter'),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
|
||||
...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
|
||||
...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/starter">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
@@ -101,7 +70,7 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-upstash-vector'
|
||||
@@ -145,7 +114,7 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-redis-labs'
|
||||
@@ -188,7 +157,7 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-neo4j-auradb'
|
||||
@@ -240,7 +209,7 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-mongo-atlas'
|
||||
@@ -277,7 +246,7 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/query-neon-postgres'
|
||||
@@ -344,25 +313,25 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/prompt-chatgpt'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/prompt_chatgpt'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/prompt-chatgpt'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['DART'],
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/prompt_chatgpt'
|
||||
@@ -405,19 +374,19 @@ return [
|
||||
'useCases' => ['messaging'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/discord-command-bot'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt && python src/setup.py',
|
||||
'src/main.py',
|
||||
'python/discord_command_bot'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['GO'],
|
||||
$templateRuntimes['GO'],
|
||||
'',
|
||||
'main.go',
|
||||
'go/discord-command-bot'
|
||||
@@ -468,7 +437,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/analyze-with-perspectiveapi'
|
||||
@@ -504,19 +473,19 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/censor-with-redact'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/censor_with_redact'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['DART'],
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/censor_with_redact'
|
||||
@@ -550,7 +519,7 @@ return [
|
||||
'timeout' => 15,
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
|
||||
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-pdf">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
@@ -573,7 +542,7 @@ return [
|
||||
'useCases' => ['dev-tools'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/github-issue-bot'
|
||||
@@ -616,7 +585,7 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/url-shortener'
|
||||
@@ -667,19 +636,19 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-algolia'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/sync_with_algolia'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/sync-with-algolia'
|
||||
@@ -748,31 +717,31 @@ return [
|
||||
'useCases' => ['databases'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-meilisearch'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/sync-with-meilisearch'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/sync-with-meilisearch'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['BUN'],
|
||||
$templateRuntimes['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/sync-with-meilisearch'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['RUBY'],
|
||||
$templateRuntimes['RUBY'],
|
||||
'bundle install',
|
||||
'lib/main.rb',
|
||||
'ruby/sync-with-meilisearch'
|
||||
@@ -841,37 +810,37 @@ return [
|
||||
'useCases' => ['messaging'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/whatsapp_with_vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['DART'],
|
||||
$templateRuntimes['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['BUN'],
|
||||
$templateRuntimes['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['RUBY'],
|
||||
$templateRuntimes['RUBY'],
|
||||
'bundle install',
|
||||
'lib/main.rb',
|
||||
'ruby/whatsapp-with-vonage'
|
||||
@@ -927,7 +896,7 @@ return [
|
||||
'useCases' => ['messaging'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/push-notification-with-fcm'
|
||||
@@ -983,19 +952,19 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/email-contact-form'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
$templateRuntimes['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
'src/main.py',
|
||||
'python/email_contact_form'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
$templateRuntimes['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/email-contact-form'
|
||||
@@ -1066,7 +1035,7 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/subscriptions-with-stripe'
|
||||
@@ -1107,7 +1076,7 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/payments-with-stripe'
|
||||
@@ -1164,7 +1133,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/text-generation-with-huggingface'
|
||||
@@ -1198,7 +1167,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/language-translation-with-huggingface'
|
||||
@@ -1232,7 +1201,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/image-classification-with-huggingface'
|
||||
@@ -1290,7 +1259,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/object-detection-with-huggingface'
|
||||
@@ -1348,7 +1317,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/speech-recognition-with-huggingface'
|
||||
@@ -1406,7 +1375,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/text-to-speech-with-huggingface'
|
||||
@@ -1464,7 +1433,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-replicate'
|
||||
@@ -1499,7 +1468,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-together-ai'
|
||||
@@ -1541,7 +1510,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/chat-with-perplexity-ai'
|
||||
@@ -1582,7 +1551,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-replicate'
|
||||
@@ -1617,7 +1586,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/sync-with-pinecone'
|
||||
@@ -1680,7 +1649,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/rag-with-langchain'
|
||||
@@ -1743,7 +1712,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/speak-with-elevenlabs'
|
||||
@@ -1798,7 +1767,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/speak-with-lmnt'
|
||||
@@ -1839,7 +1808,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/chat-with-anyscale'
|
||||
@@ -1880,7 +1849,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install && npm run setup',
|
||||
'src/main.js',
|
||||
'node/music-generation-with-huggingface'
|
||||
@@ -1922,7 +1891,7 @@ return [
|
||||
'useCases' => ['ai'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/generate-with-fal-ai'
|
||||
@@ -1957,7 +1926,7 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/subscriptions-with-lemon-squeezy'
|
||||
@@ -2012,7 +1981,7 @@ return [
|
||||
'useCases' => ['utilities'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['NODE'],
|
||||
$templateRuntimes['NODE'],
|
||||
'npm install',
|
||||
'src/main.js',
|
||||
'node/payments-with-lemon-squeezy'
|
||||
@@ -0,0 +1,753 @@
|
||||
<?php
|
||||
|
||||
use Utopia\System\System;
|
||||
|
||||
/**
|
||||
* List of Appwrite Sites templates
|
||||
*/
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
// TODO: Development override
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
}
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
const TEMPLATE_FRAMEWORKS = [
|
||||
'SVELTEKIT' => [
|
||||
'key' => 'sveltekit',
|
||||
'name' => 'SvelteKit',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'ssr',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'NEXTJS' => [
|
||||
'key' => 'nextjs',
|
||||
'name' => 'Next.js',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './.next',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'ssr',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'NUXT' => [
|
||||
'key' => 'nuxt',
|
||||
'name' => 'Nuxt',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './.output',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'ssr',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'REMIX' => [
|
||||
'key' => 'remix',
|
||||
'name' => 'Remix',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'ssr',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'ASTRO' => [
|
||||
'key' => 'astro',
|
||||
'name' => 'Astro',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './dist',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'ssr',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'FLUTTER' => [
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'installCommand' => '',
|
||||
'buildCommand' => 'flutter build web',
|
||||
'outputDirectory' => './build/web',
|
||||
'buildRuntime' => 'flutter-3.29',
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'OTHER' => [
|
||||
'key' => 'other',
|
||||
'name' => 'Other',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
'VITE' => [
|
||||
'key' => 'vite',
|
||||
'name' => 'Vite',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './dist',
|
||||
],
|
||||
'REACT' => [
|
||||
'key' => 'react',
|
||||
'name' => 'React',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './dist',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
'ANGULAR' => [
|
||||
'key' => 'angular',
|
||||
'name' => 'Angular',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './dist/angular/browser',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
'VUE' => [
|
||||
'key' => 'vue',
|
||||
'name' => 'Vue.js',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './dist',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
];
|
||||
|
||||
function getFramework(string $frameworkEnum, array $overrides)
|
||||
{
|
||||
$settings = \array_merge(TEMPLATE_FRAMEWORKS[$frameworkEnum], $overrides);
|
||||
return $settings;
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'key' => 'template-for-onelink',
|
||||
'name' => 'Onelink template',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-onelink-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-onelink-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NUXT', [
|
||||
'providerRootDirectory' => './onelink',
|
||||
'buildCommand' => 'npm run generate',
|
||||
'outputDirectory' => './dist',
|
||||
'adapter' => 'static',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'Meldiron',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-flutter',
|
||||
'name' => 'Flutter starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-flutter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-flutter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('FLUTTER', [
|
||||
'providerRootDirectory' => './',
|
||||
'buildCommand' => 'sh build.sh',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-flutter',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_PUBLIC_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-js',
|
||||
'name' => 'JavaScript starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-js-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-js-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('VITE', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-js',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-angular',
|
||||
'name' => 'Angular starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-angular-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-angular-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('ANGULAR', [
|
||||
'providerRootDirectory' => './',
|
||||
'outputDirectory' => './dist/angular-starter-kit-for-appwrite/browser',
|
||||
'buildCommand' => 'sh prepare-env.sh && npm run build'
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-angular',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-svelte',
|
||||
'name' => 'Svelte starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-svelte-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-svelte-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-svelte',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-react',
|
||||
'name' => 'React starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('REACT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-react',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-vue',
|
||||
'name' => 'Vue starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-vue-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-vue-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('VUE', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-vue',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'VITE_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-react-native',
|
||||
'name' => 'React Native starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-native-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-native-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('REACT', [
|
||||
'providerRootDirectory' => './',
|
||||
'fallbackFile' => '+not-found.html',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-react-native',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'EXPO_PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'EXPO_PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'EXPO_PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-nextjs',
|
||||
'name' => 'Next.js starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nextjs-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nextjs-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-nextjs',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-nuxt',
|
||||
'name' => 'Nuxt starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nuxt-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nuxt-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NUXT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-nuxt',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NUXT_PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NUXT_PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NUXT_PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-event',
|
||||
'name' => 'Event template',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-event-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-event-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
'installCommand' => 'pnpm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-event',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_FUNCTION_PROJECT_ID',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_FUNCTION_API_ENDPOINT',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-portfolio',
|
||||
'name' => 'Portfolio template',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-portfolio-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-portfolio-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-portfolio',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-store',
|
||||
'name' => 'Store template',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-store-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-store-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-store',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'STRIPE_SECRET_KEY',
|
||||
'description' => 'Your Stripe secret key',
|
||||
'value' => 'disabled',
|
||||
'placeholder' => 'sk_.....',
|
||||
'required' => false,
|
||||
'type' => 'password'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-blog',
|
||||
'name' => 'Blog template',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-blog-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-blog-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-blog',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'astro-starter',
|
||||
'name' => 'Astro starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('ASTRO', [
|
||||
'providerRootDirectory' => './astro/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'remix-starter',
|
||||
'name' => 'Remix starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/remix-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/remix-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('REMIX', [
|
||||
'providerRootDirectory' => './remix/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'flutter-starter',
|
||||
'name' => 'Flutter starter',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/flutter-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/flutter-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('FLUTTER', [
|
||||
'providerRootDirectory' => './flutter/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'nextjs-starter',
|
||||
'name' => 'Next.js starter website',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/nextjs-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/nextjs-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './nextjs/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'nuxt-starter',
|
||||
'name' => 'Nuxt starter website',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/nuxt-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/nuxt-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('NUXT', [
|
||||
'providerRootDirectory' => './nuxt/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'sveltekit-starter',
|
||||
'name' => 'SvelteKit starter website',
|
||||
'useCases' => ['starter'],
|
||||
'screenshotDark' => $url . '/images/sites/templates/sveltekit-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/sveltekit-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './sveltekit/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
];
|
||||
@@ -79,15 +79,6 @@ return [
|
||||
'question' => 'Enter your Appwrite hostname',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CUSTOM_DOMAIN_DENY_LIST',
|
||||
'description' => 'List of reserved or prohibited domains when configuring custom domains.',
|
||||
'introduction' => '',
|
||||
'default' => 'example.com,test.com,app.example.com',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOMAIN_FUNCTIONS',
|
||||
'description' => 'A domain to use for function preview URLs. Setting to empty turns off function preview URLs.',
|
||||
@@ -800,7 +791,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_CPUS',
|
||||
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty or 0, CPU limit will be disabled.',
|
||||
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
@@ -809,7 +800,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_MEMORY',
|
||||
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty or 0, memory limit will be disabled.',
|
||||
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
@@ -1030,22 +1021,13 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_DELAY',
|
||||
'description' => 'Deprecated with 1.6.2 use _APP_MAINTENANCE_START_TIME instead to run the maintenance at a specific time per day.',
|
||||
'description' => 'Delay value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 0 seconds.',
|
||||
'introduction' => '1.5.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_START_TIME',
|
||||
'description' => 'The time of day (in 24-hour format) when the maintenance process should start. The default value is 00:00.',
|
||||
'introduction' => '1.6.2',
|
||||
'default' => '00:00',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days).',
|
||||
@@ -1066,22 +1048,13 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
|
||||
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '1209600',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain console audit logs. The default value is 15778800 seconds (6 months).',
|
||||
'introduction' => '1.6.2',
|
||||
'default' => '15778800',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
|
||||
|
||||
+109
-90
@@ -43,7 +43,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -156,6 +155,9 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
|
||||
|
||||
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
|
||||
@@ -271,7 +273,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('expire', $expire)
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret))
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $sessionSecret) : '')
|
||||
;
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -287,7 +289,6 @@ App::post('/v1/account')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'create',
|
||||
description: '/docs/references/account/create.md',
|
||||
auth: [],
|
||||
@@ -431,7 +432,6 @@ App::get('/v1/account')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'get',
|
||||
description: '/docs/references/account/get.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -461,7 +461,6 @@ App::delete('/v1/account')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'delete',
|
||||
description: '/docs/references/account/delete.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -514,7 +513,6 @@ App::get('/v1/account/sessions')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'listSessions',
|
||||
description: '/docs/references/account/list-sessions.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -532,6 +530,9 @@ App::get('/v1/account/sessions')
|
||||
->inject('project')
|
||||
->action(function (Response $response, Document $user, Locale $locale, Document $project) {
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$current = Auth::sessionVerify($sessions, Auth::$secret);
|
||||
@@ -541,7 +542,7 @@ App::get('/v1/account/sessions')
|
||||
|
||||
$session->setAttribute('countryName', $countryName);
|
||||
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
|
||||
$session->setAttribute('secret', $session->getAttribute('secret', ''));
|
||||
$session->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '');
|
||||
|
||||
$sessions[$key] = $session;
|
||||
}
|
||||
@@ -561,7 +562,6 @@ App::delete('/v1/account/sessions')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'deleteSessions',
|
||||
description: '/docs/references/account/delete-sessions.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -631,7 +631,6 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'getSession',
|
||||
description: '/docs/references/account/get-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -650,6 +649,10 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
->inject('project')
|
||||
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) {
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = ($sessionId === 'current')
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
@@ -662,7 +665,7 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
$session
|
||||
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', $session->getAttribute('secret', ''))
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '')
|
||||
;
|
||||
|
||||
return $response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -681,7 +684,6 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'deleteSession',
|
||||
description: '/docs/references/account/delete-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -719,7 +721,9 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
continue;
|
||||
}
|
||||
|
||||
$dbForProject->deleteDocument('sessions', $session->getId());
|
||||
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
||||
return $dbForProject->deleteDocument('sessions', $session->getId());
|
||||
});
|
||||
|
||||
unset($sessions[$key]);
|
||||
|
||||
@@ -768,7 +772,6 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updateSession',
|
||||
description: '/docs/references/account/update-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -853,7 +856,6 @@ App::post('/v1/account/sessions/email')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createEmailPasswordSession',
|
||||
description: '/docs/references/account/create-session-email-password.md',
|
||||
auth: [],
|
||||
@@ -895,6 +897,10 @@ App::post('/v1/account/sessions/email')
|
||||
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
|
||||
@@ -960,7 +966,7 @@ App::post('/v1/account/sessions/email')
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
|
||||
;
|
||||
|
||||
$queueForEvents
|
||||
@@ -990,7 +996,6 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createAnonymousSession',
|
||||
description: '/docs/references/account/create-session-anonymous.md',
|
||||
auth: [],
|
||||
@@ -1014,6 +1019,9 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->inject('queueForEvents')
|
||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
|
||||
$protocol = $request->getProtocol();
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
if ('console' === $project->getId()) {
|
||||
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
|
||||
@@ -1115,7 +1123,7 @@ App::post('/v1/account/sessions/anonymous')
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
|
||||
;
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -1131,7 +1139,6 @@ App::post('/v1/account/sessions/token')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -1165,7 +1172,6 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createOAuth2Session',
|
||||
description: '/docs/references/account/create-session-oauth2.md',
|
||||
type: MethodType::WEBAUTH,
|
||||
@@ -1237,7 +1243,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('Get OAuth2 callback')
|
||||
->desc('OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'public')
|
||||
@@ -1267,7 +1273,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('Create OAuth2 callback')
|
||||
->desc('OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'public')
|
||||
@@ -1298,7 +1304,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->desc('Get OAuth2 redirect')
|
||||
->desc('OAuth2 redirect')
|
||||
->groups(['api', 'account', 'session'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -1443,7 +1449,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$userWithMatchingEmail = $dbForProject->find('users', [
|
||||
@@ -1451,7 +1457,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('$id', $userId),
|
||||
]);
|
||||
if (!empty($userWithMatchingEmail)) {
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$sessionUpgrade = true;
|
||||
@@ -1480,7 +1486,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
|
||||
if (empty($email)) {
|
||||
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1523,7 +1529,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::equal('providerEmail', [$email]),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1595,7 +1601,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||
]);
|
||||
if (!empty($identitiesWithMatchingEmail)) {
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
$dbForProject->createDocument('identities', new Document([
|
||||
@@ -1763,7 +1769,6 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createOAuth2Token',
|
||||
description: '/docs/references/account/create-token-oauth2.md',
|
||||
auth: [],
|
||||
@@ -1844,7 +1849,6 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createMagicURLToken',
|
||||
description: '/docs/references/account/create-token-magic-url.md',
|
||||
auth: [],
|
||||
@@ -1880,6 +1884,9 @@ App::post('/v1/account/tokens/magic-url')
|
||||
$phrase = Phrase::generate();
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
|
||||
if (!$result->isEmpty()) {
|
||||
@@ -1999,7 +2006,6 @@ App::post('/v1/account/tokens/magic-url')
|
||||
|
||||
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
|
||||
|
||||
$replyTo = "";
|
||||
|
||||
if ($smtpEnabled) {
|
||||
@@ -2062,11 +2068,17 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$token->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
if (!empty($phrase)) {
|
||||
$token->setAttribute('phrase', $phrase);
|
||||
}
|
||||
@@ -2086,7 +2098,6 @@ App::post('/v1/account/tokens/email')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createEmailToken',
|
||||
description: '/docs/references/account/create-token-email.md',
|
||||
auth: [],
|
||||
@@ -2099,7 +2110,7 @@ App::post('/v1/account/tokens/email')
|
||||
contentType: ContentType::JSON,
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
@@ -2120,6 +2131,10 @@ App::post('/v1/account/tokens/email')
|
||||
$phrase = Phrase::generate();
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
|
||||
if (!$result->isEmpty()) {
|
||||
$user->setAttributes($result->getArrayCopy());
|
||||
@@ -2288,11 +2303,17 @@ App::post('/v1/account/tokens/email')
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$token->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
if (!empty($phrase)) {
|
||||
$token->setAttribute('phrase', $phrase);
|
||||
}
|
||||
@@ -2312,7 +2333,6 @@ App::put('/v1/account/sessions/magic-url')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updateMagicURLSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -2350,7 +2370,6 @@ App::put('/v1/account/sessions/phone')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updatePhoneSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -2389,7 +2408,6 @@ App::post('/v1/account/tokens/phone')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createPhoneToken',
|
||||
description: '/docs/references/account/create-token-phone.md',
|
||||
auth: [],
|
||||
@@ -2421,6 +2439,10 @@ App::post('/v1/account/tokens/phone')
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
|
||||
if (!$result->isEmpty()) {
|
||||
$user->setAttributes($result->getArrayCopy());
|
||||
@@ -2572,13 +2594,14 @@ App::post('/v1/account/tokens/phone')
|
||||
}
|
||||
}
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Encode secret for clients
|
||||
$token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret));
|
||||
// Hide secret for clients
|
||||
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -2593,7 +2616,6 @@ App::post('/v1/account/jwts')
|
||||
->label('auth.type', 'jwt')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/account/create-jwt.md',
|
||||
auth: [],
|
||||
@@ -2642,7 +2664,6 @@ App::get('/v1/account/prefs')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/account/get-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2669,7 +2690,6 @@ App::get('/v1/account/logs')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/account/list-logs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2746,7 +2766,6 @@ App::patch('/v1/account/name')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/account/update-name.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2768,7 +2787,7 @@ App::patch('/v1/account/name')
|
||||
|
||||
$user->setAttribute('name', $name);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2785,7 +2804,6 @@ App::patch('/v1/account/password')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePassword',
|
||||
description: '/docs/references/account/update-password.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2843,7 +2861,7 @@ App::patch('/v1/account/password')
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2859,7 +2877,6 @@ App::patch('/v1/account/email')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/account/update-email.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2928,7 +2945,7 @@ App::patch('/v1/account/email')
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -2956,7 +2973,6 @@ App::patch('/v1/account/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePhone',
|
||||
description: '/docs/references/account/update-phone.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3014,7 +3030,7 @@ App::patch('/v1/account/phone')
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -3042,7 +3058,6 @@ App::patch('/v1/account/prefs')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/account/update-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3064,7 +3079,7 @@ App::patch('/v1/account/prefs')
|
||||
|
||||
$user->setAttribute('prefs', $prefs);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -3080,7 +3095,6 @@ App::patch('/v1/account/status')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateStatus',
|
||||
description: '/docs/references/account/update-status.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3102,7 +3116,7 @@ App::patch('/v1/account/status')
|
||||
|
||||
$user->setAttribute('status', false);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
@@ -3131,7 +3145,6 @@ App::post('/v1/account/recovery')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'recovery',
|
||||
name: 'createRecovery',
|
||||
description: '/docs/references/account/create-recovery.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3161,6 +3174,11 @@ App::post('/v1/account/recovery')
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
$url = htmlentities($url);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$email = \strtolower($email);
|
||||
|
||||
$profile = $dbForProject->findOne('users', [
|
||||
@@ -3284,13 +3302,19 @@ App::post('/v1/account/recovery')
|
||||
->setSubject($subject)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$recovery->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recovery->getId())
|
||||
->setUser($profile)
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$recovery->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -3298,7 +3322,7 @@ App::post('/v1/account/recovery')
|
||||
});
|
||||
|
||||
App::put('/v1/account/recovery')
|
||||
->desc('Update password recovery (confirmation)')
|
||||
->desc('Create password recovery (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
->label('event', 'users.[userId].recovery.[tokenId].update')
|
||||
@@ -3307,7 +3331,6 @@ App::put('/v1/account/recovery')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'recovery',
|
||||
name: 'updateRecovery',
|
||||
description: '/docs/references/account/update-recovery.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3384,7 +3407,7 @@ App::put('/v1/account/recovery')
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recoveryDocument->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
;
|
||||
|
||||
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
@@ -3398,7 +3421,6 @@ App::post('/v1/account/verification')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'createVerification',
|
||||
description: '/docs/references/account/create-email-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3432,6 +3454,9 @@ App::post('/v1/account/verification')
|
||||
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
@@ -3540,12 +3565,18 @@ App::post('/v1/account/verification')
|
||||
->setName($user->getAttribute('name') ?? '')
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$verification->setAttribute('secret', $verificationSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$verification->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -3553,7 +3584,7 @@ App::post('/v1/account/verification')
|
||||
});
|
||||
|
||||
App::put('/v1/account/verification')
|
||||
->desc('Update email verification (confirmation)')
|
||||
->desc('Create email verification (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'public')
|
||||
->label('event', 'users.[userId].verification.[tokenId].update')
|
||||
@@ -3561,7 +3592,6 @@ App::put('/v1/account/verification')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'updateVerification',
|
||||
description: '/docs/references/account/update-email-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3613,8 +3643,7 @@ App::put('/v1/account/verification')
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $userId)
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setParam('tokenId', $verification->getId());
|
||||
|
||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
@@ -3629,7 +3658,6 @@ App::post('/v1/account/verification/phone')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'createPhoneVerification',
|
||||
description: '/docs/references/account/create-phone-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3668,6 +3696,10 @@ App::post('/v1/account/verification/phone')
|
||||
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$secret = null;
|
||||
$sendSMS = true;
|
||||
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
|
||||
@@ -3756,6 +3788,7 @@ App::post('/v1/account/verification/phone')
|
||||
}
|
||||
}
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$verification->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
@@ -3763,6 +3796,11 @@ App::post('/v1/account/verification/phone')
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$verification->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($verification, Response::MODEL_TOKEN);
|
||||
@@ -3777,7 +3815,6 @@ App::put('/v1/account/verification/phone')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'updatePhoneVerification',
|
||||
description: '/docs/references/account/update-phone-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3843,7 +3880,6 @@ App::patch('/v1/account/mfa')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMFA',
|
||||
description: '/docs/references/account/update-mfa.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3866,7 +3902,7 @@ App::patch('/v1/account/mfa')
|
||||
|
||||
$user->setAttribute('mfa', $mfa);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
|
||||
if ($mfa) {
|
||||
$factors = $session->getAttribute('factors', []);
|
||||
@@ -3897,7 +3933,6 @@ App::get('/v1/account/mfa/factors')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'listMfaFactors',
|
||||
description: '/docs/references/account/list-mfa-factors.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3938,7 +3973,6 @@ App::post('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaAuthenticator',
|
||||
description: '/docs/references/account/create-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4006,7 +4040,7 @@ App::post('/v1/account/mfa/authenticators/:type')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Update authenticator (confirmation)')
|
||||
->desc('Verify authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -4015,7 +4049,6 @@ App::put('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaAuthenticator',
|
||||
description: '/docs/references/account/update-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4085,7 +4118,6 @@ App::post('/v1/account/mfa/recovery-codes')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaRecoveryCodes',
|
||||
description: '/docs/references/account/create-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4123,7 +4155,7 @@ App::post('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::patch('/v1/account/mfa/recovery-codes')
|
||||
->desc('Update MFA recovery codes (regenerate)')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -4132,7 +4164,6 @@ App::patch('/v1/account/mfa/recovery-codes')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaRecoveryCodes',
|
||||
description: '/docs/references/account/update-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4169,12 +4200,11 @@ App::patch('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::get('/v1/account/mfa/recovery-codes')
|
||||
->desc('List MFA recovery codes')
|
||||
->desc('Get MFA recovery codes')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'getMfaRecoveryCodes',
|
||||
description: '/docs/references/account/get-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4213,7 +4243,6 @@ App::delete('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'deleteMfaAuthenticator',
|
||||
description: '/docs/references/account/delete-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4259,7 +4288,6 @@ App::post('/v1/account/mfa/challenge')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaChallenge',
|
||||
description: '/docs/references/account/create-mfa-challenge.md',
|
||||
auth: [],
|
||||
@@ -4470,7 +4498,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/challenge')
|
||||
->desc('Update MFA challenge (confirmation)')
|
||||
->desc('Create MFA challenge (confirmation)')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -4479,7 +4507,6 @@ App::put('/v1/account/mfa/challenge')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaChallenge',
|
||||
description: '/docs/references/account/update-mfa-challenge.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4573,7 +4600,6 @@ App::post('/v1/account/targets/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'createPushTarget',
|
||||
description: '/docs/references/account/create-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4654,7 +4680,6 @@ App::put('/v1/account/targets/:targetId/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'updatePushTarget',
|
||||
description: '/docs/references/account/update-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4719,7 +4744,6 @@ App::delete('/v1/account/targets/:targetId/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].delete')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'deletePushTarget',
|
||||
description: '/docs/references/account/delete-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4770,7 +4794,6 @@ App::get('/v1/account/identities')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'identities',
|
||||
name: 'listIdentities',
|
||||
description: '/docs/references/account/list-identities.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4822,11 +4845,8 @@ App::get('/v1/account/identities')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$results = $dbForProject->find('identities', $queries);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$results = $dbForProject->find('identities', $queries);
|
||||
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -4845,7 +4865,6 @@ App::delete('/v1/account/identities/:identityId')
|
||||
->label('audits.userId', '{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'identities',
|
||||
name: 'deleteIdentity',
|
||||
description: '/docs/references/account/delete-identity.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
|
||||
@@ -171,7 +171,6 @@ App::get('/v1/avatars/credit-cards/:code')
|
||||
->label('cache.resource', 'avatar/credit-card')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getCreditCard',
|
||||
description: '/docs/references/avatars/get-credit-card.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -199,7 +198,6 @@ App::get('/v1/avatars/browsers/:code')
|
||||
->label('cache.resource', 'avatar/browser')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getBrowser',
|
||||
description: '/docs/references/avatars/get-browser.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -227,7 +225,6 @@ App::get('/v1/avatars/flags/:code')
|
||||
->label('cache.resource', 'avatar/flag')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getFlag',
|
||||
description: '/docs/references/avatars/get-flag.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -255,7 +252,6 @@ App::get('/v1/avatars/image')
|
||||
->label('cache.resource', 'avatar/image')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getImage',
|
||||
description: '/docs/references/avatars/get-image.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -326,7 +322,6 @@ App::get('/v1/avatars/favicon')
|
||||
->label('cache.resource', 'avatar/favicon')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getFavicon',
|
||||
description: '/docs/references/avatars/get-favicon.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -477,7 +472,6 @@ App::get('/v1/avatars/qr')
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getQR',
|
||||
description: '/docs/references/avatars/get-qr.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -526,7 +520,6 @@ App::get('/v1/avatars/initials')
|
||||
->label('cache.resource', 'avatar/initials')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getInitials',
|
||||
description: '/docs/references/avatars/get-initials.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
|
||||
@@ -27,7 +27,6 @@ App::get('/v1/console/variables')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
group: 'console',
|
||||
name: 'variables',
|
||||
description: '/docs/references/console/variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -57,23 +56,25 @@ App::get('/v1/console/variables')
|
||||
$variables = new Document([
|
||||
'_APP_DOMAIN_TARGET' => System::getEnv('_APP_DOMAIN_TARGET'),
|
||||
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
|
||||
'_APP_FUNCTIONS_SIZE_LIMIT' => +System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'),
|
||||
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
|
||||
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
|
||||
'_APP_VCS_ENABLED' => $isVcsEnabled,
|
||||
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
|
||||
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
|
||||
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
|
||||
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
|
||||
]);
|
||||
|
||||
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
|
||||
});
|
||||
|
||||
App::post('/v1/console/assistant')
|
||||
->desc('Create assistant query')
|
||||
->desc('Ask query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'assistant',
|
||||
group: 'console',
|
||||
name: 'chat',
|
||||
description: '/docs/references/assistant/chat.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
+137
-580
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,6 @@ App::get('/v1/graphql')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'get',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
hide: true,
|
||||
@@ -91,7 +90,6 @@ App::post('/v1/graphql/mutation')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'mutation',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
description: '/docs/references/graphql/post.md',
|
||||
@@ -142,7 +140,6 @@ App::post('/v1/graphql')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'query',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
description: '/docs/references/graphql/post.md',
|
||||
|
||||
@@ -32,7 +32,6 @@ App::get('/v1/health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'get',
|
||||
auth: [AuthType::KEY],
|
||||
description: '/docs/references/health/get.md',
|
||||
@@ -72,7 +71,6 @@ App::get('/v1/health/db')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getDB',
|
||||
description: '/docs/references/health/get-db.md',
|
||||
responses: [
|
||||
@@ -133,7 +131,6 @@ App::get('/v1/health/cache')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getCache',
|
||||
description: '/docs/references/health/get-cache.md',
|
||||
responses: [
|
||||
@@ -198,7 +195,6 @@ App::get('/v1/health/pubsub')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getPubSub',
|
||||
description: '/docs/references/health/get-pubsub.md',
|
||||
responses: [
|
||||
@@ -263,7 +259,6 @@ App::get('/v1/health/time')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getTime',
|
||||
description: '/docs/references/health/get-time.md',
|
||||
responses: [
|
||||
@@ -327,7 +322,6 @@ App::get('/v1/health/queue/webhooks')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueWebhooks',
|
||||
description: '/docs/references/health/get-queue-webhooks.md',
|
||||
responses: [
|
||||
@@ -360,7 +354,6 @@ App::get('/v1/health/queue/logs')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueLogs',
|
||||
description: '/docs/references/health/get-queue-logs.md',
|
||||
responses: [
|
||||
@@ -393,7 +386,6 @@ App::get('/v1/health/certificate')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getCertificate',
|
||||
description: '/docs/references/health/get-certificate.md',
|
||||
responses: [
|
||||
@@ -450,7 +442,6 @@ App::get('/v1/health/queue/certificates')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueCertificates',
|
||||
description: '/docs/references/health/get-queue-certificates.md',
|
||||
responses: [
|
||||
@@ -483,7 +474,6 @@ App::get('/v1/health/queue/builds')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueBuilds',
|
||||
description: '/docs/references/health/get-queue-builds.md',
|
||||
responses: [
|
||||
@@ -516,7 +506,6 @@ App::get('/v1/health/queue/databases')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueDatabases',
|
||||
description: '/docs/references/health/get-queue-databases.md',
|
||||
responses: [
|
||||
@@ -550,7 +539,6 @@ App::get('/v1/health/queue/deletes')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueDeletes',
|
||||
description: '/docs/references/health/get-queue-deletes.md',
|
||||
responses: [
|
||||
@@ -583,7 +571,6 @@ App::get('/v1/health/queue/mails')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMails',
|
||||
description: '/docs/references/health/get-queue-mails.md',
|
||||
responses: [
|
||||
@@ -616,7 +603,6 @@ App::get('/v1/health/queue/messaging')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMessaging',
|
||||
description: '/docs/references/health/get-queue-messaging.md',
|
||||
responses: [
|
||||
@@ -649,7 +635,6 @@ App::get('/v1/health/queue/migrations')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMigrations',
|
||||
description: '/docs/references/health/get-queue-migrations.md',
|
||||
responses: [
|
||||
@@ -682,7 +667,6 @@ App::get('/v1/health/queue/functions')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueFunctions',
|
||||
description: '/docs/references/health/get-queue-functions.md',
|
||||
responses: [
|
||||
@@ -715,7 +699,6 @@ App::get('/v1/health/queue/stats-resources')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueStatsResources',
|
||||
description: '/docs/references/health/get-queue-stats-resources.md',
|
||||
responses: [
|
||||
@@ -748,7 +731,6 @@ App::get('/v1/health/queue/stats-usage')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueUsage',
|
||||
description: '/docs/references/health/get-queue-stats-usage.md',
|
||||
responses: [
|
||||
@@ -774,6 +756,38 @@ App::get('/v1/health/queue/stats-usage')
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/stats-usage-dump')
|
||||
->desc('Get usage dump queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
name: 'getQueueStatsUsageDump',
|
||||
description: '/docs/references/health/get-queue-stats-usage-dump.md',
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_HEALTH_QUEUE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('publisher')
|
||||
->inject('response')
|
||||
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
});
|
||||
|
||||
App::get('/v1/health/storage/local')
|
||||
->desc('Get local storage')
|
||||
->groups(['api', 'health'])
|
||||
@@ -781,7 +795,6 @@ App::get('/v1/health/storage/local')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'storage',
|
||||
name: 'getStorageLocal',
|
||||
description: '/docs/references/health/get-storage-local.md',
|
||||
responses: [
|
||||
@@ -831,7 +844,6 @@ App::get('/v1/health/storage')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'storage',
|
||||
name: 'getStorage',
|
||||
description: '/docs/references/health/get-storage.md',
|
||||
responses: [
|
||||
@@ -845,9 +857,10 @@ App::get('/v1/health/storage')
|
||||
->inject('response')
|
||||
->inject('deviceForFiles')
|
||||
->inject('deviceForFunctions')
|
||||
->inject('deviceForSites')
|
||||
->inject('deviceForBuilds')
|
||||
->action(function (Response $response, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds) {
|
||||
$devices = [$deviceForFiles, $deviceForFunctions, $deviceForBuilds];
|
||||
->action(function (Response $response, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForSites, Device $deviceForBuilds) {
|
||||
$devices = [$deviceForFiles, $deviceForFunctions, $deviceForSites, $deviceForBuilds];
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
foreach ($devices as $device) {
|
||||
@@ -879,7 +892,6 @@ App::get('/v1/health/anti-virus')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getAntivirus',
|
||||
description: '/docs/references/health/get-storage-anti-virus.md',
|
||||
responses: [
|
||||
@@ -925,7 +937,6 @@ App::get('/v1/health/queue/failed/:name')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getFailedJobs',
|
||||
description: '/docs/references/health/get-failed-queue-jobs.md',
|
||||
responses: [
|
||||
@@ -944,6 +955,7 @@ App::get('/v1/health/queue/failed/:name')
|
||||
Event::FUNCTIONS_QUEUE_NAME,
|
||||
Event::STATS_RESOURCES_QUEUE_NAME,
|
||||
Event::STATS_USAGE_QUEUE_NAME,
|
||||
Event::STATS_USAGE_DUMP_QUEUE_NAME,
|
||||
Event::WEBHOOK_QUEUE_NAME,
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
@@ -969,6 +981,9 @@ App::get('/v1/health/stats') // Currently only used internally
|
||||
->desc('Get system stats')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'root')
|
||||
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
// ->label('sdk.namespace', 'health')
|
||||
// ->label('sdk.method', 'getStats')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
|
||||
@@ -17,7 +17,6 @@ App::get('/v1/locale')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'get',
|
||||
description: '/docs/references/locale/get-locale.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -81,7 +80,6 @@ App::get('/v1/locale/codes')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCodes',
|
||||
description: '/docs/references/locale/list-locale-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -107,7 +105,6 @@ App::get('/v1/locale/countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountries',
|
||||
description: '/docs/references/locale/list-countries.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -121,7 +118,7 @@ App::get('/v1/locale/countries')
|
||||
->inject('response')
|
||||
->inject('locale')
|
||||
->action(function (Response $response, Locale $locale) {
|
||||
$list = array_keys(Config::getParam('locale-countries')); /* @var $list array */
|
||||
$list = Config::getParam('locale-countries'); /* @var $list array */
|
||||
$output = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
@@ -144,7 +141,6 @@ App::get('/v1/locale/countries/eu')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountriesEU',
|
||||
description: '/docs/references/locale/list-countries-eu.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -183,7 +179,6 @@ App::get('/v1/locale/countries/phones')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountriesPhones',
|
||||
description: '/docs/references/locale/list-countries-phones.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -221,7 +216,6 @@ App::get('/v1/locale/continents')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listContinents',
|
||||
description: '/docs/references/locale/list-continents.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -235,7 +229,7 @@ App::get('/v1/locale/continents')
|
||||
->inject('response')
|
||||
->inject('locale')
|
||||
->action(function (Response $response, Locale $locale) {
|
||||
$list = array_keys(Config::getParam('locale-continents'));
|
||||
$list = Config::getParam('locale-continents');
|
||||
|
||||
foreach ($list as $value) {
|
||||
$output[] = new Document([
|
||||
@@ -257,7 +251,6 @@ App::get('/v1/locale/currencies')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCurrencies',
|
||||
description: '/docs/references/locale/list-currencies.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -284,7 +277,6 @@ App::get('/v1/locale/languages')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listLanguages',
|
||||
description: '/docs/references/locale/list-languages.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
|
||||
@@ -30,7 +30,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
@@ -64,7 +63,6 @@ App::post('/v1/messaging/providers/mailgun')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createMailgunProvider',
|
||||
description: '/docs/references/messaging/create-mailgun-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -158,7 +156,6 @@ App::post('/v1/messaging/providers/sendgrid')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createSendgridProvider',
|
||||
description: '/docs/references/messaging/create-sendgrid-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -240,7 +237,6 @@ App::post('/v1/messaging/providers/smtp')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createSmtpProvider',
|
||||
description: '/docs/references/messaging/create-smtp-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -335,7 +331,6 @@ App::post('/v1/messaging/providers/msg91')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createMsg91Provider',
|
||||
description: '/docs/references/messaging/create-msg91-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -418,7 +413,6 @@ App::post('/v1/messaging/providers/telesign')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTelesignProvider',
|
||||
description: '/docs/references/messaging/create-telesign-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -502,7 +496,6 @@ App::post('/v1/messaging/providers/textmagic')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTextmagicProvider',
|
||||
description: '/docs/references/messaging/create-textmagic-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -586,7 +579,6 @@ App::post('/v1/messaging/providers/twilio')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTwilioProvider',
|
||||
description: '/docs/references/messaging/create-twilio-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -670,7 +662,6 @@ App::post('/v1/messaging/providers/vonage')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createVonageProvider',
|
||||
description: '/docs/references/messaging/create-vonage-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -754,7 +745,6 @@ App::post('/v1/messaging/providers/fcm')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createFcmProvider',
|
||||
description: '/docs/references/messaging/create-fcm-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -824,7 +814,6 @@ App::post('/v1/messaging/providers/apns')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createApnsProvider',
|
||||
description: '/docs/references/messaging/create-apns-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -914,7 +903,6 @@ App::get('/v1/messaging/providers')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'listProviders',
|
||||
description: '/docs/references/messaging/list-providers.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -963,15 +951,10 @@ App::get('/v1/messaging/providers')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
$providers = $dbForProject->find('providers', $queries);
|
||||
$total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'providers' => $providers,
|
||||
'total' => $total,
|
||||
'providers' => $dbForProject->find('providers', $queries),
|
||||
'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_PROVIDER_LIST);
|
||||
});
|
||||
|
||||
@@ -982,7 +965,6 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'listProviderLogs',
|
||||
description: '/docs/references/messaging/list-provider-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1079,7 +1061,6 @@ App::get('/v1/messaging/providers/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'getProvider',
|
||||
description: '/docs/references/messaging/get-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1113,7 +1094,6 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateMailgunProvider',
|
||||
description: '/docs/references/messaging/update-mailgun-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1226,7 +1206,6 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateSendgridProvider',
|
||||
description: '/docs/references/messaging/update-sendgrid-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1324,7 +1303,6 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateSmtpProvider',
|
||||
description: '/docs/references/messaging/update-smtp-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1453,7 +1431,6 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateMsg91Provider',
|
||||
description: '/docs/references/messaging/update-msg91-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1540,7 +1517,6 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTelesignProvider',
|
||||
description: '/docs/references/messaging/update-telesign-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1629,7 +1605,6 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTextmagicProvider',
|
||||
description: '/docs/references/messaging/update-textmagic-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1718,7 +1693,6 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTwilioProvider',
|
||||
description: '/docs/references/messaging/update-twilio-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1807,7 +1781,6 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateVonageProvider',
|
||||
description: '/docs/references/messaging/update-vonage-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1896,7 +1869,6 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateFcmProvider',
|
||||
description: '/docs/references/messaging/update-fcm-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1972,7 +1944,6 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateApnsProvider',
|
||||
description: '/docs/references/messaging/update-apns-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2074,7 +2045,6 @@ App::delete('/v1/messaging/providers/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'deleteProvider',
|
||||
description: '/docs/references/messaging/delete-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2117,7 +2087,6 @@ App::post('/v1/messaging/topics')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'createTopic',
|
||||
description: '/docs/references/messaging/create-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2164,7 +2133,6 @@ App::get('/v1/messaging/topics')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'listTopics',
|
||||
description: '/docs/references/messaging/list-topics.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2213,15 +2181,10 @@ App::get('/v1/messaging/topics')
|
||||
|
||||
$cursor->setValue($cursorDocument[0]);
|
||||
}
|
||||
try {
|
||||
$topics = $dbForProject->find('topics', $queries);
|
||||
$total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'topics' => $topics,
|
||||
'total' => $total,
|
||||
'topics' => $dbForProject->find('topics', $queries),
|
||||
'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_TOPIC_LIST);
|
||||
});
|
||||
|
||||
@@ -2232,7 +2195,6 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'listTopicLogs',
|
||||
description: '/docs/references/messaging/list-topic-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2330,7 +2292,6 @@ App::get('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'getTopic',
|
||||
description: '/docs/references/messaging/get-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2365,7 +2326,6 @@ App::patch('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'updateTopic',
|
||||
description: '/docs/references/messaging/update-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2416,7 +2376,6 @@ App::delete('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'deleteTopic',
|
||||
description: '/docs/references/messaging/delete-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2464,7 +2423,6 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'createSubscriber',
|
||||
description: '/docs/references/messaging/create-subscriber.md',
|
||||
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2564,7 +2522,6 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'listSubscribers',
|
||||
description: '/docs/references/messaging/list-subscribers.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2622,11 +2579,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||
|
||||
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
|
||||
return function () use ($subscriber, $dbForProject) {
|
||||
@@ -2653,7 +2607,6 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'listSubscriberLogs',
|
||||
description: '/docs/references/messaging/list-subscriber-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2751,7 +2704,6 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'getSubscriber',
|
||||
description: '/docs/references/messaging/get-subscriber.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2800,7 +2752,6 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'deleteSubscriber',
|
||||
description: '/docs/references/messaging/delete-subscriber.md',
|
||||
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2867,7 +2818,6 @@ App::post('/v1/messaging/messages/email')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createEmail',
|
||||
description: '/docs/references/messaging/create-email.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2986,7 +2936,7 @@ App::post('/v1/messaging/messages/email')
|
||||
break;
|
||||
case MessageStatus::SCHEDULED:
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -3026,7 +2976,6 @@ App::post('/v1/messaging/messages/sms')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createSms',
|
||||
description: '/docs/references/messaging/create-sms.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3109,7 +3058,7 @@ App::post('/v1/messaging/messages/sms')
|
||||
break;
|
||||
case MessageStatus::SCHEDULED:
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -3149,7 +3098,6 @@ App::post('/v1/messaging/messages/push')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createPush',
|
||||
description: '/docs/references/messaging/create-push.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3327,7 +3275,7 @@ App::post('/v1/messaging/messages/push')
|
||||
break;
|
||||
case MessageStatus::SCHEDULED:
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -3364,7 +3312,6 @@ App::get('/v1/messaging/messages')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'listMessages',
|
||||
description: '/docs/references/messaging/list-messages.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3413,15 +3360,10 @@ App::get('/v1/messaging/messages')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
$messages = $dbForProject->find('messages', $queries);
|
||||
$total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'messages' => $messages,
|
||||
'total' => $total,
|
||||
'messages' => $dbForProject->find('messages', $queries),
|
||||
'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_MESSAGE_LIST);
|
||||
});
|
||||
|
||||
@@ -3432,7 +3374,6 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'logs',
|
||||
name: 'listMessageLogs',
|
||||
description: '/docs/references/messaging/list-message-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3530,7 +3471,6 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'listTargets',
|
||||
description: '/docs/references/messaging/list-message-targets.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3593,15 +3533,10 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
$targets = $dbForProject->find('targets', $queries);
|
||||
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'targets' => $targets,
|
||||
'total' => $total,
|
||||
'targets' => $dbForProject->find('targets', $queries),
|
||||
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
@@ -3612,7 +3547,6 @@ App::get('/v1/messaging/messages/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'getMessage',
|
||||
description: '/docs/references/messaging/get-message.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3646,7 +3580,6 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/messaging/update-email.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3728,7 +3661,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
|
||||
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -3853,7 +3786,6 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updateSms',
|
||||
description: '/docs/references/messaging/update-sms.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3930,7 +3862,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
|
||||
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -4015,7 +3947,6 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updatePush',
|
||||
description: '/docs/references/messaging/update-push.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -4104,7 +4035,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
|
||||
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
|
||||
$schedule = $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
@@ -4275,7 +4206,6 @@ App::delete('/v1/messaging/messages/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'delete',
|
||||
description: '/docs/references/messaging/delete-message.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
|
||||
@@ -12,7 +12,6 @@ use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
@@ -32,13 +31,12 @@ include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
App::post('/v1/migrations/appwrite')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Appwrite migration')
|
||||
->desc('Migrate Appwrite data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createAppwriteMigration',
|
||||
description: '/docs/references/migrations/migration-appwrite.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -94,13 +92,12 @@ App::post('/v1/migrations/appwrite')
|
||||
|
||||
App::post('/v1/migrations/firebase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Firebase migration')
|
||||
->desc('Migrate Firebase data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createFirebaseMigration',
|
||||
description: '/docs/references/migrations/migration-firebase.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -161,13 +158,12 @@ App::post('/v1/migrations/firebase')
|
||||
|
||||
App::post('/v1/migrations/supabase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Supabase migration')
|
||||
->desc('Migrate Supabase data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createSupabaseMigration',
|
||||
description: '/docs/references/migrations/migration-supabase.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -228,13 +224,12 @@ App::post('/v1/migrations/supabase')
|
||||
|
||||
App::post('/v1/migrations/nhost')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create NHost migration')
|
||||
->desc('Migrate NHost data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createNHostMigration',
|
||||
description: '/docs/references/migrations/migration-nhost.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -301,7 +296,6 @@ App::get('/v1/migrations')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'list',
|
||||
description: '/docs/references/migrations/list-migrations.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -353,15 +347,10 @@ App::get('/v1/migrations')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$migrations = $dbForProject->find('migrations', $queries);
|
||||
$total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'migrations' => $migrations,
|
||||
'total' => $total,
|
||||
'migrations' => $dbForProject->find('migrations', $queries),
|
||||
'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_MIGRATION_LIST);
|
||||
});
|
||||
|
||||
@@ -371,7 +360,6 @@ App::get('/v1/migrations/:migrationId')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'get',
|
||||
description: '/docs/references/migrations/get-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -397,11 +385,10 @@ App::get('/v1/migrations/:migrationId')
|
||||
|
||||
App::get('/v1/migrations/appwrite/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Appwrite migration report')
|
||||
->desc('Generate a report on Appwrite data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getAppwriteReport',
|
||||
description: '/docs/references/migrations/migration-appwrite-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -421,7 +408,6 @@ App::get('/v1/migrations/appwrite/report')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
|
||||
|
||||
$appwrite = new Appwrite($projectID, $endpoint, $key);
|
||||
|
||||
try {
|
||||
@@ -446,11 +432,10 @@ App::get('/v1/migrations/appwrite/report')
|
||||
|
||||
App::get('/v1/migrations/firebase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Firebase migration report')
|
||||
->desc('Generate a report on Firebase data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getFirebaseReport',
|
||||
description: '/docs/references/migrations/migration-firebase-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -499,11 +484,10 @@ App::get('/v1/migrations/firebase/report')
|
||||
|
||||
App::get('/v1/migrations/supabase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Supabase migration report')
|
||||
->desc('Generate a report on Supabase Data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getSupabaseReport',
|
||||
description: '/docs/references/migrations/migration-supabase-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -548,11 +532,10 @@ App::get('/v1/migrations/supabase/report')
|
||||
|
||||
App::get('/v1/migrations/nhost/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get NHost migration report')
|
||||
->desc('Generate a report on NHost Data')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getNHostReport',
|
||||
description: '/docs/references/migrations/migration-nhost-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -597,14 +580,13 @@ App::get('/v1/migrations/nhost/report')
|
||||
|
||||
App::patch('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Update retry migration')
|
||||
->desc('Retry migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].retry')
|
||||
->label('audits.event', 'migration.retry')
|
||||
->label('audits.resource', 'migrations/{request.migrationId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'retry',
|
||||
description: '/docs/references/migrations/retry-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -655,7 +637,6 @@ App::delete('/v1/migrations/:migrationId')
|
||||
->label('audits.resource', 'migrations/{request.migrationId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'delete',
|
||||
description: '/docs/references/migrations/delete-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -17,6 +17,7 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
@@ -26,7 +27,6 @@ App::get('/v1/project/usage')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/project/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -388,7 +388,6 @@ App::post('/v1/project/variables')
|
||||
->label('audits.event', 'variable.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'createVariable',
|
||||
description: '/docs/references/project/create-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -401,11 +400,12 @@ App::post('/v1/project/variables')
|
||||
))
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $key, string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $key, string $value, bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
$variableId = ID::unique();
|
||||
|
||||
$variable = new Document([
|
||||
@@ -420,6 +420,7 @@ App::post('/v1/project/variables')
|
||||
'resourceType' => 'project',
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'secret' => $secret,
|
||||
'search' => implode(' ', [$variableId, $key, 'project']),
|
||||
]);
|
||||
|
||||
@@ -448,7 +449,6 @@ App::get('/v1/project/variables')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'listVariables',
|
||||
description: '/docs/references/project/list-variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -479,7 +479,6 @@ App::get('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'getVariable',
|
||||
description: '/docs/references/project/get-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -509,7 +508,6 @@ App::put('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'updateVariable',
|
||||
description: '/docs/references/project/update-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -523,19 +521,25 @@ App::put('/v1/project/variables/:variableId')
|
||||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $variableId, string $key, ?string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $variableId, string $key, ?string $value, ?bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceType') !== 'project') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $key, 'project']));
|
||||
|
||||
try {
|
||||
@@ -561,7 +565,6 @@ App::delete('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'deleteVariable',
|
||||
description: '/docs/references/project/delete-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -28,7 +28,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -69,7 +68,6 @@ App::post('/v1/projects')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'create',
|
||||
description: '/docs/references/projects/create.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -140,14 +138,6 @@ App::post('/v1/projects')
|
||||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
|
||||
if ($region !== 'default') {
|
||||
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
|
||||
$keys = explode(',', $databaseKeys);
|
||||
$databases = array_filter($keys, function ($value) use ($region) {
|
||||
return str_contains($value, $region);
|
||||
});
|
||||
}
|
||||
|
||||
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
||||
$index = \array_search($databaseOverride, $databases);
|
||||
if ($index !== false) {
|
||||
@@ -215,17 +205,17 @@ App::post('/v1/projects')
|
||||
$dsn = new DSN('mysql://' . $dsn);
|
||||
}
|
||||
|
||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
|
||||
|
||||
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
|
||||
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
|
||||
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
|
||||
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
|
||||
|
||||
if (!$sharedTablesV2) {
|
||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
|
||||
if ($sharedTables) {
|
||||
$dbForProject
|
||||
->setSharedTables(true)
|
||||
@@ -307,7 +297,6 @@ App::get('/v1/projects')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'list',
|
||||
description: '/docs/references/projects/list.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -360,15 +349,10 @@ App::get('/v1/projects')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$projects = $dbForPlatform->find('projects', $queries);
|
||||
$total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'projects' => $projects,
|
||||
'total' => $total,
|
||||
'projects' => $dbForPlatform->find('projects', $queries),
|
||||
'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_PROJECT_LIST);
|
||||
});
|
||||
|
||||
@@ -378,7 +362,6 @@ App::get('/v1/projects/:projectId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'get',
|
||||
description: '/docs/references/projects/get.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -411,7 +394,6 @@ App::patch('/v1/projects/:projectId')
|
||||
->label('audits.resource', 'project/{request.projectId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'update',
|
||||
description: '/docs/references/projects/update.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -465,7 +447,6 @@ App::patch('/v1/projects/:projectId/team')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateTeam',
|
||||
description: '/docs/references/projects/update-team.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -540,7 +521,6 @@ App::patch('/v1/projects/:projectId/service')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateServiceStatus',
|
||||
description: '/docs/references/projects/update-service-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -578,7 +558,6 @@ App::patch('/v1/projects/:projectId/service/all')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateServiceStatusAll',
|
||||
description: '/docs/references/projects/update-service-status-all.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -619,7 +598,6 @@ App::patch('/v1/projects/:projectId/api')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateApiStatus',
|
||||
description: '/docs/references/projects/update-api-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -657,7 +635,6 @@ App::patch('/v1/projects/:projectId/api/all')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateApiStatusAll',
|
||||
description: '/docs/references/projects/update-api-status-all.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -698,7 +675,6 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateOAuth2',
|
||||
description: '/docs/references/projects/update-oauth2.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -749,7 +725,6 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateSessionAlerts',
|
||||
description: '/docs/references/projects/update-session-alerts.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -787,7 +762,6 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateMembershipsPrivacy',
|
||||
description: '/docs/references/projects/update-memberships-privacy.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -829,7 +803,6 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthLimit',
|
||||
description: '/docs/references/projects/update-auth-limit.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -867,7 +840,6 @@ App::patch('/v1/projects/:projectId/auth/duration')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthDuration',
|
||||
description: '/docs/references/projects/update-auth-duration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -905,7 +877,6 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthStatus',
|
||||
description: '/docs/references/projects/update-auth-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -946,7 +917,6 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthPasswordHistory',
|
||||
description: '/docs/references/projects/update-auth-password-history.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -984,7 +954,6 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthPasswordDictionary',
|
||||
description: '/docs/references/projects/update-auth-password-dictionary.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1017,12 +986,11 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||
->desc('Update personal data check')
|
||||
->desc('Enable or disable checking user passwords for similarity with their personal data.')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updatePersonalDataCheck',
|
||||
description: '/docs/references/projects/update-personal-data-check.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1060,7 +1028,6 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthSessionsLimit',
|
||||
description: '/docs/references/projects/update-auth-sessions-limit.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1098,7 +1065,6 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateMockNumbers',
|
||||
description: '/docs/references/projects/update-mock-numbers.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1146,7 +1112,6 @@ App::delete('/v1/projects/:projectId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'delete',
|
||||
description: '/docs/references/projects/delete.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1190,7 +1155,6 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'createWebhook',
|
||||
description: '/docs/references/projects/create-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1255,7 +1219,6 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'listWebhooks',
|
||||
description: '/docs/references/projects/list-webhooks.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1294,7 +1257,6 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'getWebhook',
|
||||
description: '/docs/references/projects/get-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1335,7 +1297,6 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'updateWebhook',
|
||||
description: '/docs/references/projects/update-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1401,7 +1362,6 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'updateWebhookSignature',
|
||||
description: '/docs/references/projects/update-webhook-signature.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1447,7 +1407,6 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'deleteWebhook',
|
||||
description: '/docs/references/projects/delete-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1495,7 +1454,6 @@ App::post('/v1/projects/:projectId/keys')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'createKey',
|
||||
description: '/docs/references/projects/create-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1552,7 +1510,6 @@ App::get('/v1/projects/:projectId/keys')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'listKeys',
|
||||
description: '/docs/references/projects/list-keys.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1591,7 +1548,6 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'getKey',
|
||||
description: '/docs/references/projects/get-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1632,7 +1588,6 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'updateKey',
|
||||
description: '/docs/references/projects/update-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1685,7 +1640,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'deleteKey',
|
||||
description: '/docs/references/projects/delete-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1733,7 +1687,6 @@ App::post('/v1/projects/:projectId/jwts')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/projects/create-jwt.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1777,7 +1730,6 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'createPlatform',
|
||||
description: '/docs/references/projects/create-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1834,7 +1786,6 @@ App::get('/v1/projects/:projectId/platforms')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'listPlatforms',
|
||||
description: '/docs/references/projects/list-platforms.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1873,7 +1824,6 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'getPlatform',
|
||||
description: '/docs/references/projects/get-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1914,7 +1864,6 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'updatePlatform',
|
||||
description: '/docs/references/projects/update-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1970,7 +1919,6 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'deletePlatform',
|
||||
description: '/docs/references/projects/delete-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2018,7 +1966,6 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateSmtp',
|
||||
description: '/docs/references/projects/update-smtp.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2115,7 +2062,6 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'createSmtpTest',
|
||||
description: '/docs/references/projects/create-smtp-test.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2182,7 +2128,6 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'getSmsTemplate',
|
||||
description: '/docs/references/projects/get-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2230,7 +2175,6 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'getEmailTemplate',
|
||||
description: '/docs/references/projects/get-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2289,7 +2233,6 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateSmsTemplate',
|
||||
description: '/docs/references/projects/update-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2336,7 +2279,6 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateEmailTemplate',
|
||||
description: '/docs/references/projects/update-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2393,7 +2335,6 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'deleteSmsTemplate',
|
||||
description: '/docs/references/projects/delete-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2439,12 +2380,11 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->desc('Delete custom email template')
|
||||
->desc('Reset custom email template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'deleteEmailTemplate',
|
||||
description: '/docs/references/projects/delete-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -15,169 +15,13 @@ use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Domain as ValidatorDomain;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/proxy/rules')
|
||||
->groups(['api', 'proxy'])
|
||||
->desc('Create rule')
|
||||
->label('scope', 'rules.write')
|
||||
->label('event', 'rules.[ruleId].create')
|
||||
->label('audits.event', 'rule.create')
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'createRule',
|
||||
description: '/docs/references/proxy/create-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_PROXY_RULE,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('domain', null, new ValidatorDomain(), 'Domain name.')
|
||||
->param('resourceType', null, new WhiteList(['api', 'function']), 'Action definition for the rule. Possible values are "api", "function"')
|
||||
->param('resourceId', '', new UID(), 'ID of resource for the action type. If resourceType is "api", leave empty. If resourceType is "function", provide ID of the function.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('queueForCertificates')
|
||||
->inject('queueForEvents')
|
||||
->inject('dbForPlatform')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) {
|
||||
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
if ($domain === $mainDomain) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
|
||||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
|
||||
$denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST');
|
||||
|
||||
if (!empty($denyListDomains)) {
|
||||
$functionsDomain .= ',' . $denyListDomains;
|
||||
}
|
||||
|
||||
$deniedDomains = array_map('trim', explode(',', $functionsDomain));
|
||||
|
||||
foreach ($deniedDomains as $deniedDomain) {
|
||||
if (str_ends_with($domain, $deniedDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or its subdomain to a specific resource. Please use a different domain.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
|
||||
}
|
||||
|
||||
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
|
||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||
$document = $dbForPlatform->getDocument('rules', md5($domain));
|
||||
} else {
|
||||
$document = $dbForPlatform->findOne('rules', [
|
||||
Query::equal('domain', [$domain]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
if (!$document->isEmpty()) {
|
||||
if ($document->getAttribute('projectId') === $project->getId()) {
|
||||
$resourceType = $document->getAttribute('resourceType');
|
||||
$resourceId = $document->getAttribute('resourceId');
|
||||
$message = "Domain already assigned to '{$resourceType}' service";
|
||||
if (!empty($resourceId)) {
|
||||
$message .= " with ID '{$resourceId}'";
|
||||
}
|
||||
|
||||
$message .= '.';
|
||||
} else {
|
||||
$message = 'Domain already assigned to different project.';
|
||||
}
|
||||
|
||||
throw new Exception(Exception::RULE_ALREADY_EXISTS, $message);
|
||||
}
|
||||
|
||||
$resourceInternalId = '';
|
||||
|
||||
if ($resourceType == 'function') {
|
||||
if (empty($resourceId)) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $resourceId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$resourceInternalId = $function->getInternalId();
|
||||
}
|
||||
|
||||
try {
|
||||
$domain = new Domain($domain);
|
||||
} catch (\Throwable) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
|
||||
}
|
||||
|
||||
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
|
||||
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique();
|
||||
|
||||
$rule = new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $domain->get(),
|
||||
'resourceType' => $resourceType,
|
||||
'resourceId' => $resourceId,
|
||||
'resourceInternalId' => $resourceInternalId,
|
||||
'certificateId' => '',
|
||||
'owner' => '',
|
||||
'region' => $project->getAttribute('region')
|
||||
]);
|
||||
|
||||
$status = 'created';
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
|
||||
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
|
||||
$status = 'verified';
|
||||
}
|
||||
|
||||
if ($status === 'created') {
|
||||
$target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
$validator = new CNAME($target->get()); // Verify Domain with DNS records
|
||||
|
||||
if ($validator->isValid($domain->get())) {
|
||||
$status = 'verifying';
|
||||
|
||||
$queueForCertificates
|
||||
->setDomain(new Document([
|
||||
'domain' => $rule->getAttribute('domain')
|
||||
]))
|
||||
->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
$rule->setAttribute('status', $status);
|
||||
$rule = $dbForPlatform->createDocument('rules', $rule);
|
||||
|
||||
$queueForEvents->setParam('ruleId', $rule->getId());
|
||||
|
||||
$rule->setAttribute('logs', '');
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($rule, Response::MODEL_PROXY_RULE);
|
||||
});
|
||||
|
||||
App::get('/v1/proxy/rules')
|
||||
->groups(['api', 'proxy'])
|
||||
@@ -185,7 +29,6 @@ App::get('/v1/proxy/rules')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'listRules',
|
||||
description: '/docs/references/proxy/list-rules.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -260,7 +103,6 @@ App::get('/v1/proxy/rules/:ruleId')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'getRule',
|
||||
description: '/docs/references/proxy/get-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -298,7 +140,6 @@ App::delete('/v1/proxy/rules/:ruleId')
|
||||
->label('audits.resource', 'rule/{request.ruleId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'deleteRule',
|
||||
description: '/docs/references/proxy/delete-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -343,7 +184,6 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'updateRuleVerification',
|
||||
description: '/docs/references/proxy/update-rule-verification.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -24,7 +24,6 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\NotFound as NotFoundException;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -63,7 +62,6 @@ App::post('/v1/storage/buckets')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'createBucket',
|
||||
description: '/docs/references/storage/create-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -166,7 +164,6 @@ App::get('/v1/storage/buckets')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'listBuckets',
|
||||
description: '/docs/references/storage/list-buckets.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -219,15 +216,10 @@ App::get('/v1/storage/buckets')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$buckets = $dbForProject->find('buckets', $queries);
|
||||
$total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'buckets' => $buckets,
|
||||
'total' => $total,
|
||||
'buckets' => $dbForProject->find('buckets', $queries),
|
||||
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_BUCKET_LIST);
|
||||
});
|
||||
|
||||
@@ -238,7 +230,6 @@ App::get('/v1/storage/buckets/:bucketId')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'getBucket',
|
||||
description: '/docs/references/storage/get-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -273,7 +264,6 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'updateBucket',
|
||||
description: '/docs/references/storage/update-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -344,7 +334,6 @@ App::delete('/v1/storage/buckets/:bucketId')
|
||||
->label('audits.resource', 'bucket/{request.bucketId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'deleteBucket',
|
||||
description: '/docs/references/storage/delete-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -398,18 +387,17 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'createFile',
|
||||
description: '/docs/references/storage/create-file.md',
|
||||
type: MethodType::UPLOAD,
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
requestType: 'multipart/form-data',
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_FILE,
|
||||
)
|
||||
],
|
||||
type: MethodType::UPLOAD,
|
||||
requestType: ContentType::MULTIPART
|
||||
]
|
||||
))
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
@@ -768,7 +756,6 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'listFiles',
|
||||
description: '/docs/references/storage/list-files.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -850,8 +837,6 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -868,7 +853,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFile',
|
||||
description: '/docs/references/storage/get-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -925,7 +909,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->label('cache.resource', 'file/{request.fileId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFilePreview',
|
||||
description: '/docs/references/storage/get-file-preview.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1108,7 +1091,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFileDownload',
|
||||
description: '/docs/references/storage/get-file-download.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1257,7 +1239,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFileView',
|
||||
description: '/docs/references/storage/get-file-view.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1415,6 +1396,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', '*/*')
|
||||
->label('sdk.methodType', 'location')
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
|
||||
@@ -1575,7 +1559,6 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'updateFile',
|
||||
description: '/docs/references/storage/update-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1690,7 +1673,6 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'deleteFile',
|
||||
description: '/docs/references/storage/delete-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1790,7 +1772,6 @@ App::get('/v1/storage/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/storage/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1877,7 +1858,6 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: null,
|
||||
name: 'getBucketUsage',
|
||||
description: '/docs/references/storage/get-bucket-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -33,7 +33,6 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -63,7 +62,6 @@ App::post('/v1/teams')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'create',
|
||||
description: '/docs/references/teams/create-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -154,7 +152,6 @@ App::get('/v1/teams')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'list',
|
||||
description: '/docs/references/teams/list-teams.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -207,12 +204,9 @@ App::get('/v1/teams')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$results = $dbForProject->find('teams', $queries);
|
||||
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$results = $dbForProject->find('teams', $queries);
|
||||
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'teams' => $results,
|
||||
@@ -226,7 +220,6 @@ App::get('/v1/teams/:teamId')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'get',
|
||||
description: '/docs/references/teams/get-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -257,7 +250,6 @@ App::get('/v1/teams/:teamId/prefs')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/teams/get-team-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -293,7 +285,6 @@ App::put('/v1/teams/:teamId')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/teams/update-team-name.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -322,7 +313,9 @@ App::put('/v1/teams/:teamId')
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$teamId, $name]));
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
$team = $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $team) {
|
||||
return $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
});
|
||||
|
||||
$queueForEvents->setParam('teamId', $team->getId());
|
||||
|
||||
@@ -339,7 +332,6 @@ App::put('/v1/teams/:teamId/prefs')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/teams/update-team-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -379,7 +371,6 @@ App::delete('/v1/teams/:teamId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'delete',
|
||||
description: '/docs/references/teams/delete-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -438,7 +429,6 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'createMembership',
|
||||
description: '/docs/references/teams/create-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -625,10 +615,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
$membership = ($isPrivilegedUser || $isAppUser) ?
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
|
||||
$dbForProject->createDocument('memberships', $membership);
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
}
|
||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
|
||||
} elseif ($membership->getAttribute('confirm') === false) {
|
||||
$membership->setAttribute('secret', Auth::hash($secret));
|
||||
@@ -806,7 +793,6 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'listMemberships',
|
||||
description: '/docs/references/teams/list-team-members.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -870,20 +856,17 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
);
|
||||
$total = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: $filterQueries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
);
|
||||
|
||||
$total = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: $filterQueries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
|
||||
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
@@ -948,7 +931,6 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'getMembership',
|
||||
description: '/docs/references/teams/get-team-member.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1035,7 +1017,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'updateMembership',
|
||||
description: '/docs/references/teams/update-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1050,6 +1031,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('roles', [], function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
@@ -1061,10 +1043,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
if ($team->isEmpty()) {
|
||||
@@ -1085,21 +1066,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
// Quick check: fetch up to 2 owners to determine if only one exists
|
||||
$ownersCount = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: [Query::contains('roles', ['owner'])],
|
||||
max: 2
|
||||
);
|
||||
|
||||
// Prevent role change if there's only one owner left,
|
||||
// the requester is that owner, and the new `$roles` no longer include 'owner'!
|
||||
if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
|
||||
}
|
||||
@@ -1139,7 +1105,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'updateMembershipStatus',
|
||||
description: '/docs/references/teams/update-team-membership-status.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -1297,7 +1262,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'deleteMembership',
|
||||
description: '/docs/references/teams/delete-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1368,7 +1332,6 @@ App::get('/v1/teams/:teamId/logs')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/teams/get-team-logs.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -9,8 +9,6 @@ use Appwrite\Auth\Validator\PasswordDictionary;
|
||||
use Appwrite\Auth\Validator\PasswordHistory;
|
||||
use Appwrite\Auth\Validator\PersonalData;
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Deletes\Identities as DeleteIdentities;
|
||||
use Appwrite\Deletes\Targets as DeleteTargets;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -35,7 +33,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -194,7 +191,6 @@ App::post('/v1/users')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'create',
|
||||
description: '/docs/references/users/create-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -229,7 +225,6 @@ App::post('/v1/users/bcrypt')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createBcryptUser',
|
||||
description: '/docs/references/users/create-bcrypt-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -264,7 +259,6 @@ App::post('/v1/users/md5')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createMD5User',
|
||||
description: '/docs/references/users/create-md5-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -299,7 +293,6 @@ App::post('/v1/users/argon2')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createArgon2User',
|
||||
description: '/docs/references/users/create-argon2-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -334,7 +327,6 @@ App::post('/v1/users/sha')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createSHAUser',
|
||||
description: '/docs/references/users/create-sha-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -376,7 +368,6 @@ App::post('/v1/users/phpass')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createPHPassUser',
|
||||
description: '/docs/references/users/create-phpass-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -411,7 +402,6 @@ App::post('/v1/users/scrypt')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createScryptUser',
|
||||
description: '/docs/references/users/create-scrypt-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -459,7 +449,6 @@ App::post('/v1/users/scrypt-modified')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createScryptModifiedUser',
|
||||
description: '/docs/references/users/create-scrypt-modified-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -498,7 +487,6 @@ App::post('/v1/users/:userId/targets')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'createTarget',
|
||||
description: '/docs/references/users/create-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -590,7 +578,6 @@ App::get('/v1/users')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'list',
|
||||
description: '/docs/references/users/list-users.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -643,15 +630,10 @@ App::get('/v1/users')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$users = $dbForProject->find('users', $queries);
|
||||
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'users' => $users,
|
||||
'total' => $total,
|
||||
'users' => $dbForProject->find('users', $queries),
|
||||
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_USER_LIST);
|
||||
});
|
||||
|
||||
@@ -661,7 +643,6 @@ App::get('/v1/users/:userId')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'get',
|
||||
description: '/docs/references/users/get-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -692,7 +673,6 @@ App::get('/v1/users/:userId/prefs')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/users/get-user-prefs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -725,7 +705,6 @@ App::get('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'getTarget',
|
||||
description: '/docs/references/users/get-user-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -763,7 +742,6 @@ App::get('/v1/users/:userId/sessions')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'listSessions',
|
||||
description: '/docs/references/users/list-user-sessions.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -810,7 +788,6 @@ App::get('/v1/users/:userId/memberships')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'memberships',
|
||||
name: 'listMemberships',
|
||||
description: '/docs/references/users/list-user-memberships.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -855,7 +832,6 @@ App::get('/v1/users/:userId/logs')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/users/list-user-logs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -952,7 +928,6 @@ App::get('/v1/users/:userId/targets')
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'listTargets',
|
||||
description: '/docs/references/users/list-user-targets.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -1005,15 +980,10 @@ App::get('/v1/users/:userId/targets')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
try {
|
||||
$targets = $dbForProject->find('targets', $queries);
|
||||
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'targets' => $targets,
|
||||
'total' => $total,
|
||||
'targets' => $dbForProject->find('targets', $queries),
|
||||
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
@@ -1023,7 +993,6 @@ App::get('/v1/users/identities')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'identities',
|
||||
name: 'listIdentities',
|
||||
description: '/docs/references/users/list-identities.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1076,15 +1045,10 @@ App::get('/v1/users/identities')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$identities = $dbForProject->find('identities', $queries);
|
||||
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'identities' => $identities,
|
||||
'total' => $total,
|
||||
'identities' => $dbForProject->find('identities', $queries),
|
||||
'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_IDENTITY_LIST);
|
||||
});
|
||||
|
||||
@@ -1098,7 +1062,6 @@ App::patch('/v1/users/:userId/status')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateStatus',
|
||||
description: '/docs/references/users/update-user-status.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1139,7 +1102,6 @@ App::put('/v1/users/:userId/labels')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateLabels',
|
||||
description: '/docs/references/users/update-user-labels.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1182,7 +1144,6 @@ App::patch('/v1/users/:userId/verification/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePhoneVerification',
|
||||
description: '/docs/references/users/update-user-phone-verification.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1224,7 +1185,6 @@ App::patch('/v1/users/:userId/name')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/users/update-user-name.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1267,7 +1227,6 @@ App::patch('/v1/users/:userId/password')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePassword',
|
||||
description: '/docs/references/users/update-user-password.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1350,7 +1309,6 @@ App::patch('/v1/users/:userId/email')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/users/update-user-email.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1450,7 +1408,6 @@ App::patch('/v1/users/:userId/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePhone',
|
||||
description: '/docs/references/users/update-user-phone.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1540,7 +1497,6 @@ App::patch('/v1/users/:userId/verification')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateEmailVerification',
|
||||
description: '/docs/references/users/update-user-email-verification.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1578,7 +1534,6 @@ App::patch('/v1/users/:userId/prefs')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/users/update-user-prefs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1619,7 +1574,6 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'updateTarget',
|
||||
description: '/docs/references/users/update-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -1724,7 +1678,6 @@ App::patch('/v1/users/:userId/mfa')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateMfa',
|
||||
description: '/docs/references/users/update-user-mfa.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1764,7 +1717,6 @@ App::get('/v1/users/:userId/mfa/factors')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'listMfaFactors',
|
||||
description: '/docs/references/users/list-mfa-factors.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1803,7 +1755,6 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'getMfaRecoveryCodes',
|
||||
description: '/docs/references/users/get-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1848,7 +1799,6 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'createMfaRecoveryCodes',
|
||||
description: '/docs/references/users/create-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1890,7 +1840,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Update MFA recovery codes (regenerate)')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
||||
->label('scope', 'users.write')
|
||||
@@ -1900,7 +1850,6 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaRecoveryCodes',
|
||||
description: '/docs/references/users/update-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1951,7 +1900,6 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'deleteMfaAuthenticator',
|
||||
description: '/docs/references/users/delete-mfa-authenticator.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1999,7 +1947,6 @@ App::post('/v1/users/:userId/sessions')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createSession',
|
||||
description: '/docs/references/users/create-session.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2084,7 +2031,6 @@ App::post('/v1/users/:userId/tokens')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createToken',
|
||||
description: '/docs/references/users/create-token.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2147,7 +2093,6 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'deleteSession',
|
||||
description: '/docs/references/users/delete-user-session.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2198,7 +2143,6 @@ App::delete('/v1/users/:userId/sessions')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'deleteSessions',
|
||||
description: '/docs/references/users/delete-user-sessions.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2248,7 +2192,6 @@ App::delete('/v1/users/:userId')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'delete',
|
||||
description: '/docs/references/users/delete.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2277,8 +2220,6 @@ App::delete('/v1/users/:userId')
|
||||
$clone = clone $user;
|
||||
|
||||
$dbForProject->deleteDocument('users', $userId);
|
||||
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
|
||||
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
@@ -2300,7 +2241,6 @@ App::delete('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'deleteTarget',
|
||||
description: '/docs/references/users/delete-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -2358,7 +2298,6 @@ App::delete('/v1/users/identities/:identityId')
|
||||
->label('audits.resource', 'identity/{request.$identityId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'identities',
|
||||
name: 'deleteIdentity',
|
||||
description: '/docs/references/users/delete-identity.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2398,7 +2337,6 @@ App::post('/v1/users/:userId/jwts')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/users/create-user-jwt.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2454,7 +2392,6 @@ App::get('/v1/users/usage')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/users/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
+359
-129
@@ -14,12 +14,11 @@ use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Vcs\Comment;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -27,22 +26,35 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Detector\Adapter\Bun;
|
||||
use Utopia\Detector\Adapter\CPP;
|
||||
use Utopia\Detector\Adapter\Dart;
|
||||
use Utopia\Detector\Adapter\Deno;
|
||||
use Utopia\Detector\Adapter\Dotnet;
|
||||
use Utopia\Detector\Adapter\Java;
|
||||
use Utopia\Detector\Adapter\JavaScript;
|
||||
use Utopia\Detector\Adapter\PHP;
|
||||
use Utopia\Detector\Adapter\Python;
|
||||
use Utopia\Detector\Adapter\Ruby;
|
||||
use Utopia\Detector\Adapter\Swift;
|
||||
use Utopia\Detector\Detector;
|
||||
use Utopia\Detector\Detection\Framework\Astro;
|
||||
use Utopia\Detector\Detection\Framework\Flutter;
|
||||
use Utopia\Detector\Detection\Framework\NextJs;
|
||||
use Utopia\Detector\Detection\Framework\Nuxt;
|
||||
use Utopia\Detector\Detection\Framework\Remix;
|
||||
use Utopia\Detector\Detection\Framework\SvelteKit;
|
||||
use Utopia\Detector\Detection\Packager\NPM;
|
||||
use Utopia\Detector\Detection\Packager\PNPM;
|
||||
use Utopia\Detector\Detection\Packager\Yarn;
|
||||
use Utopia\Detector\Detection\Runtime\Bun;
|
||||
use Utopia\Detector\Detection\Runtime\CPP;
|
||||
use Utopia\Detector\Detection\Runtime\Dart;
|
||||
use Utopia\Detector\Detection\Runtime\Deno;
|
||||
use Utopia\Detector\Detection\Runtime\Dotnet;
|
||||
use Utopia\Detector\Detection\Runtime\Java;
|
||||
use Utopia\Detector\Detection\Runtime\Node;
|
||||
use Utopia\Detector\Detection\Runtime\PHP;
|
||||
use Utopia\Detector\Detection\Runtime\Python;
|
||||
use Utopia\Detector\Detection\Runtime\Ruby;
|
||||
use Utopia\Detector\Detection\Runtime\Swift;
|
||||
use Utopia\Detector\Detector\Framework;
|
||||
use Utopia\Detector\Detector\Packager;
|
||||
use Utopia\Detector\Detector\Runtime;
|
||||
use Utopia\Detector\Detector\Strategy;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
@@ -50,29 +62,30 @@ use function Swoole\Coroutine\batch;
|
||||
|
||||
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Build $queueForBuilds, callable $getProjectDB, Request $request) {
|
||||
$errors = [];
|
||||
foreach ($repositories as $resource) {
|
||||
foreach ($repositories as $repository) {
|
||||
try {
|
||||
$resourceType = $resource->getAttribute('resourceType');
|
||||
$resourceType = $repository->getAttribute('resourceType');
|
||||
|
||||
if ($resourceType !== "function") {
|
||||
if ($resourceType !== "function" && $resourceType !== "site") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$projectId = $resource->getAttribute('projectId');
|
||||
$projectId = $repository->getAttribute('projectId');
|
||||
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
$functionId = $resource->getAttribute('resourceId');
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
$functionInternalId = $function->getInternalId();
|
||||
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
|
||||
$resourceId = $repository->getAttribute('resourceId');
|
||||
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
|
||||
$resourceInternalId = $resource->getInternalId();
|
||||
|
||||
$deploymentId = ID::unique();
|
||||
$repositoryId = $resource->getId();
|
||||
$repositoryInternalId = $resource->getInternalId();
|
||||
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
|
||||
$installationId = $resource->getAttribute('installationId');
|
||||
$installationInternalId = $resource->getAttribute('installationInternalId');
|
||||
$productionBranch = $function->getAttribute('providerBranch');
|
||||
$repositoryId = $repository->getId();
|
||||
$repositoryInternalId = $repository->getInternalId();
|
||||
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
|
||||
$installationId = $repository->getAttribute('installationId');
|
||||
$installationInternalId = $repository->getAttribute('installationInternalId');
|
||||
$productionBranch = $resource->getAttribute('providerBranch');
|
||||
$activate = false;
|
||||
|
||||
if ($providerBranch == $productionBranch && $external === false) {
|
||||
@@ -96,7 +109,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$isAuthorized = !$external;
|
||||
|
||||
if (!$isAuthorized && !empty($providerPullRequestId)) {
|
||||
if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) {
|
||||
if (\in_array($providerPullRequestId, $repository->getAttribute('providerPullRequestIds', []))) {
|
||||
$isAuthorized = true;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +122,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
|
||||
$latestCommentId = '';
|
||||
|
||||
if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false) === false) {
|
||||
if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) {
|
||||
$latestComment = Authorization::skip(fn () => $dbForPlatform->findOne('vcsComments', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::equal('providerPullRequestId', [$providerPullRequestId]),
|
||||
@@ -118,14 +131,15 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
|
||||
if (!$latestComment->isEmpty()) {
|
||||
$latestCommentId = $latestComment->getAttribute('providerCommentId', '');
|
||||
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
||||
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
|
||||
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
|
||||
} else {
|
||||
$comment = new Comment();
|
||||
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
$latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment()));
|
||||
|
||||
if (!empty($latestCommentId)) {
|
||||
@@ -162,19 +176,19 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$latestCommentId = $comment->getAttribute('providerCommentId', '');
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
||||
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
|
||||
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isAuthorized) {
|
||||
$functionName = $function->getAttribute('name');
|
||||
$resourceName = $resource->getAttribute('name');
|
||||
$projectName = $project->getAttribute('name');
|
||||
$name = "{$functionName} ({$projectName})";
|
||||
$name = "{$resourceName} ({$projectName})";
|
||||
$message = 'Authorization required for external contributor.';
|
||||
|
||||
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
|
||||
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
@@ -194,6 +208,17 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$providerRepositoryOwner = $pullRequestResponse['head']['repo']['name'];
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
if (!empty($resource->getAttribute('installCommand', ''))) {
|
||||
$commands[] = $resource->getAttribute('installCommand', '');
|
||||
}
|
||||
if (!empty($resource->getAttribute('buildCommand', ''))) {
|
||||
$commands[] = $resource->getAttribute('buildCommand', '');
|
||||
}
|
||||
if (!empty($resource->getAttribute('commands', ''))) {
|
||||
$commands[] = $resource->getAttribute('commands', '');
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
@@ -201,11 +226,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $functionId,
|
||||
'resourceInternalId' => $functionInternalId,
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $function->getAttribute('entrypoint'),
|
||||
'commands' => $function->getAttribute('commands'),
|
||||
'resourceId' => $resourceId,
|
||||
'resourceInternalId' => $resourceInternalId,
|
||||
'resourceType' => $resourceCollection,
|
||||
'entrypoint' => $resource->getAttribute('entrypoint', ''),
|
||||
'buildCommands' => \implode(' && ', $commands),
|
||||
'buildOutput' => $resource->getAttribute('outputDirectory', ''),
|
||||
'type' => 'vcs',
|
||||
'installationId' => $installationId,
|
||||
'installationInternalId' => $installationInternalId,
|
||||
@@ -219,21 +245,107 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
'providerCommitHash' => $providerCommitHash,
|
||||
'providerCommitAuthorUrl' => $providerCommitAuthorUrl,
|
||||
'providerCommitAuthor' => $providerCommitAuthor,
|
||||
'providerCommitMessage' => $providerCommitMessage,
|
||||
'providerCommitMessage' => mb_strimwidth($providerCommitMessage, 0, 255, '...'),
|
||||
'providerCommitUrl' => $providerCommitUrl,
|
||||
'providerCommentId' => \strval($latestCommentId),
|
||||
'providerBranch' => $providerBranch,
|
||||
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]),
|
||||
'search' => implode(' ', [$deploymentId, $resource->getAttribute('entrypoint', '')]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) {
|
||||
$functionName = $function->getAttribute('name');
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$projectId = $project->getId();
|
||||
|
||||
// Deployment preview
|
||||
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
|
||||
$domain = ID::unique() . "." . $sitesDomain;
|
||||
$ruleId = md5($domain);
|
||||
Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
'deploymentVcsProviderBranch' => $providerBranch,
|
||||
'status' => 'verified',
|
||||
'certificateId' => '',
|
||||
'search' => implode(' ', [$ruleId, $domain]),
|
||||
]))
|
||||
);
|
||||
|
||||
// VCS branch preview
|
||||
if (!empty($providerBranch)) {
|
||||
$domain = "branch-{$providerBranch}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
|
||||
$ruleId = md5($domain);
|
||||
try {
|
||||
Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
'deploymentVcsProviderBranch' => $providerBranch,
|
||||
'status' => 'verified',
|
||||
'certificateId' => '',
|
||||
'search' => implode(' ', [$ruleId, $domain]),
|
||||
]))
|
||||
);
|
||||
} catch (Duplicate $err) {
|
||||
// Ignore, rule already exists; will be updated by builds worker
|
||||
}
|
||||
}
|
||||
|
||||
// VCS commit preview
|
||||
if (!empty($providerCommitHash)) {
|
||||
$domain = "commit-{$providerCommitHash}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
|
||||
$ruleId = md5($domain);
|
||||
try {
|
||||
Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
'deploymentVcsProviderBranch' => $providerBranch,
|
||||
'status' => 'verified',
|
||||
'certificateId' => '',
|
||||
'search' => implode(' ', [$ruleId, $domain]),
|
||||
]))
|
||||
);
|
||||
} catch (Duplicate $err) {
|
||||
// Ignore, rule already exists; will be updated by builds worker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($providerCommitHash) && $resource->getAttribute('providerSilentMode', false) === false) {
|
||||
$resourceName = $resource->getAttribute('name');
|
||||
$projectName = $project->getAttribute('name');
|
||||
$name = "{$functionName} ({$projectName})";
|
||||
$name = "{$resourceName} ({$projectName})";
|
||||
$message = 'Starting...';
|
||||
|
||||
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
|
||||
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
@@ -244,17 +356,17 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
}
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId";
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
|
||||
}
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setResource($resource)
|
||||
->setDeployment($deployment)
|
||||
->setProject($project); // set the project because it won't be set for git deployments
|
||||
|
||||
$queueForBuilds->trigger(); // must trigger here so that we create a build for each function
|
||||
$queueForBuilds->trigger(); // must trigger here so that we create a build for each function/site
|
||||
|
||||
//TODO: Add event?
|
||||
} catch (Throwable $e) {
|
||||
@@ -270,13 +382,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
};
|
||||
|
||||
App::get('/v1/vcs/github/authorize')
|
||||
->desc('Create GitHub app installation')
|
||||
->desc('Install GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'createGitHubInstallation',
|
||||
description: '/docs/references/vcs/create-github-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -320,7 +431,7 @@ App::get('/v1/vcs/github/authorize')
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/callback')
|
||||
->desc('Get installation and authorization from GitHub app')
|
||||
->desc('Capture installation and authorization from GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -458,7 +569,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepositoryContents',
|
||||
description: '/docs/references/vcs/get-repository-contents.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -519,31 +629,35 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
]), Response::MODEL_VCS_CONTENT_LIST);
|
||||
});
|
||||
|
||||
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
||||
->desc('Create runtime settings detection')
|
||||
App::post('/v1/vcs/github/installations/:installationId/detections')
|
||||
->alias('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
||||
->desc('Create repository detection')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepositoryDetection',
|
||||
description: '/docs/references/vcs/create-repository-detection.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_DETECTION,
|
||||
model: Response::MODEL_DETECTION_RUNTIME,
|
||||
),
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_DETECTION_FRAMEWORK,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('providerRootDirectory', '', new Text(256, 0), 'Path to Root Directory', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
|
||||
->action(function (string $installationId, string $providerRepositoryId, string $type, string $providerRootDirectory, GitHub $github, Response $response, Database $dbForPlatform) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
@@ -569,32 +683,108 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
|
||||
$files = \array_column($files, 'name');
|
||||
$languages = $github->listRepositoryLanguages($owner, $repositoryName);
|
||||
|
||||
$detectorFactory = new Detector($files, $languages);
|
||||
$detector = new Packager($files);
|
||||
$detector
|
||||
->addOption(new Yarn())
|
||||
->addOption(new PNPM())
|
||||
->addOption(new NPM());
|
||||
$detection = $detector->detect();
|
||||
|
||||
$detectorFactory
|
||||
->addDetector(new JavaScript())
|
||||
->addDetector(new Bun())
|
||||
->addDetector(new PHP())
|
||||
->addDetector(new Python())
|
||||
->addDetector(new Dart())
|
||||
->addDetector(new Swift())
|
||||
->addDetector(new Ruby())
|
||||
->addDetector(new Java())
|
||||
->addDetector(new CPP())
|
||||
->addDetector(new Deno())
|
||||
->addDetector(new Dotnet());
|
||||
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
|
||||
|
||||
$runtime = $detectorFactory->detect();
|
||||
if ($type === 'framework') {
|
||||
$output = new Document([
|
||||
'framework' => '',
|
||||
'installCommand' => '',
|
||||
'buildCommand' => '',
|
||||
'outputDirectory' => '',
|
||||
]);
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeDetail = \array_reverse(\array_filter(\array_keys($runtimes), function ($key) use ($runtime, $runtimes) {
|
||||
return $runtimes[$key]['key'] === $runtime;
|
||||
}))[0] ?? '';
|
||||
$detector = new Framework($files, $packager);
|
||||
$detector
|
||||
->addOption(new Flutter())
|
||||
->addOption(new Nuxt())
|
||||
->addOption(new Astro())
|
||||
->addOption(new SvelteKit())
|
||||
->addOption(new NextJs())
|
||||
->addOption(new Remix());
|
||||
|
||||
$detection = [];
|
||||
$detection['runtime'] = $runtimeDetail;
|
||||
$framework = $detector->detect();
|
||||
|
||||
$response->dynamic(new Document($detection), Response::MODEL_DETECTION);
|
||||
if (!\is_null($framework)) {
|
||||
$output->setAttribute('installCommand', $framework->getInstallCommand());
|
||||
$output->setAttribute('buildCommand', $framework->getBuildCommand());
|
||||
$output->setAttribute('outputDirectory', $framework->getOutputDirectory());
|
||||
$framework = $framework->getName();
|
||||
} else {
|
||||
$framework = 'other';
|
||||
$output->setAttribute('installCommand', '');
|
||||
$output->setAttribute('buildCommand', '');
|
||||
$output->setAttribute('outputDirectory', '');
|
||||
}
|
||||
|
||||
$frameworks = Config::getParam('frameworks');
|
||||
if (!\in_array($framework, array_keys($frameworks), true)) {
|
||||
$framework = 'other';
|
||||
}
|
||||
$output->setAttribute('framework', $framework);
|
||||
} else {
|
||||
$output = new Document([
|
||||
'runtime' => '',
|
||||
'commands' => '',
|
||||
'entrypoint' => '',
|
||||
]);
|
||||
|
||||
$strategies = [
|
||||
new Strategy(Strategy::FILEMATCH),
|
||||
new Strategy(Strategy::LANGUAGES),
|
||||
new Strategy(Strategy::EXTENSION),
|
||||
];
|
||||
|
||||
foreach ($strategies as $strategy) {
|
||||
$detector = new Runtime($strategy === Strategy::LANGUAGES ? $languages : $files, $strategy, $packager);
|
||||
$detector
|
||||
->addOption(new Node())
|
||||
->addOption(new Bun())
|
||||
->addOption(new Deno())
|
||||
->addOption(new PHP())
|
||||
->addOption(new Python())
|
||||
->addOption(new Dart())
|
||||
->addOption(new Swift())
|
||||
->addOption(new Ruby())
|
||||
->addOption(new Java())
|
||||
->addOption(new CPP())
|
||||
->addOption(new Dotnet());
|
||||
|
||||
$runtime = $detector->detect();
|
||||
|
||||
if (!\is_null($runtime)) {
|
||||
$output->setAttribute('commands', $runtime->getCommands());
|
||||
$output->setAttribute('entrypoint', $runtime->getEntrypoint());
|
||||
$runtime = $runtime->getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($runtime)) {
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeWithVersion = '';
|
||||
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
|
||||
if ($runtimeConfig['key'] === $runtime) {
|
||||
$runtimeWithVersion = $runtimeKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($runtimeWithVersion)) {
|
||||
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
|
||||
}
|
||||
|
||||
$output->setAttribute('runtime', $runtimeWithVersion);
|
||||
} else {
|
||||
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
|
||||
}
|
||||
}
|
||||
$response->dynamic($output, $type === 'framework' ? Response::MODEL_DETECTION_FRAMEWORK : Response::MODEL_DETECTION_RUNTIME);
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
@@ -603,24 +793,27 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositories',
|
||||
description: '/docs/references/vcs/list-repositories.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY_LIST,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST,
|
||||
),
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $installationId, string $search, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
|
||||
->action(function (string $installationId, string $type, string $search, GitHub $github, Response $response, Database $dbForPlatform) {
|
||||
if (empty($search)) {
|
||||
$search = "";
|
||||
}
|
||||
@@ -650,39 +843,86 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
return $repo;
|
||||
}, $repos);
|
||||
|
||||
$repos = batch(\array_map(function ($repo) use ($github) {
|
||||
return function () use ($repo, $github) {
|
||||
try {
|
||||
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
|
||||
$files = \array_column($files, 'name');
|
||||
$repos = batch(\array_map(function ($repo) use ($type, $github) {
|
||||
return function () use ($repo, $type, $github) {
|
||||
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
|
||||
$files = \array_column($files, 'name');
|
||||
|
||||
$detector = new Packager($files);
|
||||
$detector
|
||||
->addOption(new Yarn())
|
||||
->addOption(new PNPM())
|
||||
->addOption(new NPM());
|
||||
$detection = $detector->detect();
|
||||
|
||||
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
|
||||
|
||||
if ($type === 'framework') {
|
||||
$frameworkDetector = new Framework($files, $packager);
|
||||
$frameworkDetector
|
||||
->addOption(new Flutter())
|
||||
->addOption(new Nuxt())
|
||||
->addOption(new Astro())
|
||||
->addOption(new SvelteKit())
|
||||
->addOption(new NextJs())
|
||||
->addOption(new Remix());
|
||||
|
||||
$detectedFramework = $frameworkDetector->detect();
|
||||
|
||||
if (!\is_null($detectedFramework)) {
|
||||
$framework = $detectedFramework->getName();
|
||||
} else {
|
||||
$framework = 'other';
|
||||
}
|
||||
|
||||
$frameworks = Config::getParam('frameworks');
|
||||
if (!\in_array($framework, array_keys($frameworks), true)) {
|
||||
$framework = 'other';
|
||||
}
|
||||
$repo['framework'] = $framework;
|
||||
} else {
|
||||
$languages = $github->listRepositoryLanguages($repo['organization'], $repo['name']);
|
||||
|
||||
$detectorFactory = new Detector($files, $languages);
|
||||
$strategies = [
|
||||
new Strategy(Strategy::FILEMATCH),
|
||||
new Strategy(Strategy::LANGUAGES),
|
||||
new Strategy(Strategy::EXTENSION),
|
||||
];
|
||||
|
||||
$detectorFactory
|
||||
->addDetector(new JavaScript())
|
||||
->addDetector(new Bun())
|
||||
->addDetector(new PHP())
|
||||
->addDetector(new Python())
|
||||
->addDetector(new Dart())
|
||||
->addDetector(new Swift())
|
||||
->addDetector(new Ruby())
|
||||
->addDetector(new Java())
|
||||
->addDetector(new CPP())
|
||||
->addDetector(new Deno())
|
||||
->addDetector(new Dotnet());
|
||||
foreach ($strategies as $strategy) {
|
||||
$detector = new Runtime($strategy === Strategy::LANGUAGES ? $languages : $files, $strategy, $packager);
|
||||
$detector
|
||||
->addOption(new Node())
|
||||
->addOption(new Bun())
|
||||
->addOption(new Deno())
|
||||
->addOption(new PHP())
|
||||
->addOption(new Python())
|
||||
->addOption(new Dart())
|
||||
->addOption(new Swift())
|
||||
->addOption(new Ruby())
|
||||
->addOption(new Java())
|
||||
->addOption(new CPP())
|
||||
->addOption(new Dotnet());
|
||||
|
||||
$runtime = $detectorFactory->detect();
|
||||
$runtime = $detector->detect();
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeDetail = \array_reverse(\array_filter(\array_keys($runtimes), function ($key) use ($runtime, $runtimes) {
|
||||
return $runtimes[$key]['key'] === $runtime;
|
||||
}))[0] ?? '';
|
||||
if (!\is_null($runtime)) {
|
||||
$runtime = $runtime->getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$repo['runtime'] = $runtimeDetail;
|
||||
} catch (Throwable $error) {
|
||||
$repo['runtime'] = "";
|
||||
Console::warning("Runtime not detected for " . $repo['organization'] . "/" . $repo['name']);
|
||||
if (!empty($runtime)) {
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeWithVersion = '';
|
||||
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
|
||||
if ($runtimeConfig['key'] === $runtime) {
|
||||
$runtimeWithVersion = $runtimeKey;
|
||||
}
|
||||
}
|
||||
|
||||
$repo['runtime'] = $runtimeWithVersion ?? '';
|
||||
}
|
||||
}
|
||||
return $repo;
|
||||
};
|
||||
@@ -693,9 +933,9 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
}, $repos);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'providerRepositories' => $repos,
|
||||
$type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos,
|
||||
'total' => \count($repos),
|
||||
]), Response::MODEL_PROVIDER_REPOSITORY_LIST);
|
||||
]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
@@ -704,7 +944,6 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepository',
|
||||
description: '/docs/references/vcs/create-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -817,7 +1056,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepository',
|
||||
description: '/docs/references/vcs/get-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -872,7 +1110,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositoryBranches',
|
||||
description: '/docs/references/vcs/list-repository-branches.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -965,7 +1202,7 @@ App::post('/v1/vcs/github/events')
|
||||
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
//find functionId from functions table
|
||||
//find resourceId from relevant resources table
|
||||
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
|
||||
Query::equal('providerRepositoryId', [$providerRepositoryId]),
|
||||
Query::limit(100),
|
||||
@@ -977,7 +1214,7 @@ App::post('/v1/vcs/github/events')
|
||||
}
|
||||
} elseif ($event == $github::EVENT_INSTALLATION) {
|
||||
if ($parsedPayload["action"] == "deleted") {
|
||||
// TODO: Use worker for this job instead (update function as well)
|
||||
// TODO: Use worker for this job instead (update function/site as well)
|
||||
$providerInstallationId = $parsedPayload["installationId"];
|
||||
|
||||
$installations = $dbForPlatform->find('installations', [
|
||||
@@ -1066,7 +1303,6 @@ App::get('/v1/vcs/installations')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'listInstallations',
|
||||
description: '/docs/references/vcs/list-installations.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1122,12 +1358,9 @@ App::get('/v1/vcs/installations')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$results = $dbForPlatform->find('installations', $queries);
|
||||
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$results = $dbForPlatform->find('installations', $queries);
|
||||
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'installations' => $results,
|
||||
@@ -1141,7 +1374,6 @@ App::get('/v1/vcs/installations/:installationId')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'getInstallation',
|
||||
description: '/docs/references/vcs/get-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1176,7 +1408,6 @@ App::delete('/v1/vcs/installations/:installationId')
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'deleteInstallation',
|
||||
description: '/docs/references/vcs/delete-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1212,12 +1443,11 @@ App::delete('/v1/vcs/installations/:installationId')
|
||||
});
|
||||
|
||||
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
|
||||
->desc('Update external deployment (authorize)')
|
||||
->desc('Authorize external deployment')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'updateExternalDeployments',
|
||||
description: '/docs/references/vcs/update-external-deployments.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
+381
-169
@@ -3,25 +3,30 @@
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Transformation\Adapter\Preview;
|
||||
use Appwrite\Transformation\Transformation;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
|
||||
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
||||
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
|
||||
use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
|
||||
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
|
||||
use Appwrite\Utopia\Response\Filters\V18 as ResponseV18;
|
||||
use Appwrite\Utopia\Response\Filters\V19 as ResponseV19;
|
||||
use Appwrite\Utopia\View;
|
||||
use Executor\Executor;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -42,6 +47,7 @@ use Utopia\Logger\Adapter\Sentry;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\User;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator\Text;
|
||||
@@ -50,7 +56,7 @@ Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey)
|
||||
{
|
||||
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
|
||||
|
||||
@@ -61,9 +67,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
|
||||
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
|
||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||
$route = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
|
||||
} else {
|
||||
$route = Authorization::skip(
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [$host]),
|
||||
Query::limit(1)
|
||||
@@ -71,20 +77,12 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
)[0] ?? new Document();
|
||||
}
|
||||
|
||||
if ($route->isEmpty()) {
|
||||
|
||||
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
|
||||
$appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
|
||||
if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) {
|
||||
$appDomainFunctions = $appDomainFunctionsFallback;
|
||||
}
|
||||
|
||||
if ($host === $appDomainFunctions) {
|
||||
if ($rule->isEmpty()) {
|
||||
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '') || $host === System::getEnv('_APP_DOMAIN_SITES', '')) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
|
||||
}
|
||||
|
||||
if (\str_ends_with($host, $appDomainFunctions)) {
|
||||
if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) || \str_ends_with($host, System::getEnv('_APP_DOMAIN_SITES', ''))) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.');
|
||||
}
|
||||
|
||||
@@ -96,10 +94,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
|
||||
// Act as API - no Proxy logic
|
||||
$utopia->getRoute()?->label('error', '');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$projectId = $route->getAttribute('projectId');
|
||||
$projectId = $rule->getAttribute('projectId');
|
||||
$project = Authorization::skip(
|
||||
fn () => $dbForPlatform->getDocument('projects', $projectId)
|
||||
);
|
||||
@@ -125,45 +124,34 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $route->getAttribute('resourceType');
|
||||
$type = $rule->getAttribute('type', '');
|
||||
|
||||
if ($type === 'function') {
|
||||
$method = $utopia->getRoute()?->getLabel('sdk', null);
|
||||
|
||||
if (empty($method)) {
|
||||
$utopia->getRoute()?->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'createExecution',
|
||||
description: '/docs/references/functions/create-execution.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_EXECUTION,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::MULTIPART,
|
||||
));
|
||||
} else {
|
||||
/** @var Method $method */
|
||||
$method->setNamespace('functions');
|
||||
$method->setMethodName('createExecution');
|
||||
$utopia->getRoute()?->label('sdk', $method);
|
||||
}
|
||||
|
||||
if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||
if ($request->getProtocol() !== 'https') {
|
||||
if ($type === 'deployment') {
|
||||
if (System::getEnv('_APP_OPTIONS_COMPUTE_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||
if ($request->getProtocol() !== 'https' && $request->getHostname() !== APP_HOSTNAME_INTERNAL) {
|
||||
if ($request->getMethod() !== Request::METHOD_GET) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
|
||||
}
|
||||
|
||||
return $response->redirect('https://' . $request->getHostname() . $request->getURI());
|
||||
}
|
||||
}
|
||||
|
||||
$functionId = $route->getAttribute('resourceId');
|
||||
$projectId = $route->getAttribute('projectId');
|
||||
/** @var Database $dbForProject */
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
|
||||
|
||||
if ($deployment->getAttribute('resourceType', '') === 'functions') {
|
||||
$type = 'function';
|
||||
} elseif ($deployment->getAttribute('resourceType', '') === 'sites') {
|
||||
$type = 'site';
|
||||
}
|
||||
|
||||
$resource = $type === 'function' ?
|
||||
Authorization::skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
|
||||
Authorization::skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
|
||||
|
||||
$isPreview = $type === 'function' ? false : ($rule->getAttribute('trigger', '') !== 'manual');
|
||||
|
||||
$path = ($swooleRequest->server['request_uri'] ?? '/');
|
||||
$query = ($swooleRequest->server['query_string'] ?? '');
|
||||
@@ -171,78 +159,143 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$path .= '?' . $query;
|
||||
}
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
/**
|
||||
Ensure preview authorization
|
||||
- Authorization is skippable for tests, and build screenshot
|
||||
- If cookie is not sent by client -> not authorized
|
||||
- If JWT in cookie is invalid or expired -> not authorized
|
||||
- If user is blocked or removed -> not authorized
|
||||
- If user's session is removed or expired -> not authorized
|
||||
- If user is not member of team of this deployment -> not authorized
|
||||
- If not authorized, redirect to Console redirect UI
|
||||
- If authorized, continue as if auth was not required
|
||||
*/
|
||||
$requirePreview = \is_null($apiKey) || !$apiKey->isPreviewAuthDisabled();
|
||||
if ($isPreview && $requirePreview) {
|
||||
$cookie = $request->getCookie(Auth::$cookieNamePreview, '');
|
||||
$authorized = false;
|
||||
|
||||
// Security checks to mark authorized true
|
||||
if (!empty($cookie)) {
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||
|
||||
$payload = [];
|
||||
try {
|
||||
$payload = $jwt->decode($cookie);
|
||||
} catch (JWTException $error) {
|
||||
// Authorized remains false
|
||||
}
|
||||
|
||||
$userExists = false;
|
||||
$userId = $payload['userId'] ?? '';
|
||||
if (!empty($userId)) {
|
||||
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
|
||||
if (!$user->isEmpty() && $user->getAttribute('status', false)) {
|
||||
$userExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
$sessionExists = false;
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
if (!empty($jwtSessionId) && !empty($user->find('$id', $jwtSessionId, 'sessions'))) {
|
||||
$sessionExists = true;
|
||||
}
|
||||
|
||||
$membershipExists = false;
|
||||
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
|
||||
if (!$project->isEmpty() && isset($user)) {
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
$membership = $user->find('teamId', $teamId, 'memberships');
|
||||
if (!empty($membership)) {
|
||||
$membershipExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($userExists && $sessionExists && $membershipExists) {
|
||||
$authorized = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$authorized) {
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_DOMAIN');
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($url . '/console/auth/preview?'
|
||||
. \http_build_query([
|
||||
'projectId' => $projectId,
|
||||
'origin' => $protocol . '://' . $host,
|
||||
'path' => $path
|
||||
]));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$body = $swooleRequest->getContent() ?? '';
|
||||
$method = $swooleRequest->server['request_method'];
|
||||
|
||||
$requestHeaders = $request->getHeaders();
|
||||
|
||||
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
|
||||
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty() || !$function->getAttribute('enabled')) {
|
||||
if ($resource->isEmpty() || !$resource->getAttribute('enabled')) {
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($isResourceBlocked($project, RESOURCE_TYPE_FUNCTIONS, $functionId)) {
|
||||
if ($isResourceBlocked($project, $type === 'function' ? RESOURCE_TYPE_FUNCTIONS : RESOURCE_TYPE_SITES, $resource->getId())) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_RESOURCE_BLOCKED);
|
||||
}
|
||||
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
|
||||
$version = match ($type) {
|
||||
'function' => $resource->getAttribute('version', 'v2'),
|
||||
'site' => 'v5',
|
||||
};
|
||||
|
||||
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
|
||||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
|
||||
$runtime = match ($type) {
|
||||
'function' => $runtimes[$resource->getAttribute('runtime')] ?? null,
|
||||
'site' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null,
|
||||
default => null
|
||||
};
|
||||
|
||||
// Static site enforced runtime
|
||||
if ($resource->getAttribute('adapter', '') === 'static') {
|
||||
$runtime = $runtimes['static-1'] ?? null;
|
||||
}
|
||||
|
||||
if (\is_null($runtime)) {
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
|
||||
|
||||
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
|
||||
throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
||||
}
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function');
|
||||
}
|
||||
|
||||
/** Check if build has completed */
|
||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
||||
if ($build->isEmpty()) {
|
||||
throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
if ($deployment->getAttribute('status') !== 'ready') {
|
||||
throw new AppwriteException(AppwriteException::BUILD_NOT_READY);
|
||||
}
|
||||
|
||||
$permissions = $function->getAttribute('execute');
|
||||
|
||||
if (!(\in_array('any', $permissions)) && !(\in_array('guests', $permissions))) {
|
||||
throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"');
|
||||
if ($type === 'function') {
|
||||
$permissions = $resource->getAttribute('execute');
|
||||
if (!(\in_array('any', $permissions)) && !(\in_array('guests', $permissions))) {
|
||||
throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"');
|
||||
}
|
||||
}
|
||||
|
||||
$jwtExpiry = $function->getAttribute('timeout', 900);
|
||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||
$apiKey = $jwtObj->encode([
|
||||
'projectId' => $project->getId(),
|
||||
'scopes' => $function->getAttribute('scopes', [])
|
||||
]);
|
||||
|
||||
$headers = \array_merge([], $requestHeaders);
|
||||
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
|
||||
$headers['x-appwrite-trigger'] = 'http';
|
||||
$headers['x-appwrite-user-id'] = '';
|
||||
$headers['x-appwrite-user-jwt'] = '';
|
||||
$headers['x-appwrite-country-code'] = '';
|
||||
$headers['x-appwrite-continent-code'] = '';
|
||||
$headers['x-appwrite-continent-eu'] = 'false';
|
||||
|
||||
$jwtExpiry = $resource->getAttribute('timeout', 900);
|
||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||
$jwtKey = $jwtObj->encode([
|
||||
'projectId' => $project->getId(),
|
||||
'scopes' => $resource->getAttribute('scopes', [])
|
||||
]);
|
||||
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $jwtKey;
|
||||
$headers['x-appwrite-trigger'] = 'http';
|
||||
$headers['x-appwrite-user-jwt'] = '';
|
||||
|
||||
$ip = $headers['x-real-ip'] ?? '';
|
||||
if (!empty($ip)) {
|
||||
$record = $geodb->get($ip);
|
||||
@@ -268,12 +321,10 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => [],
|
||||
'functionInternalId' => $function->getInternalId(),
|
||||
'functionId' => $function->getId(),
|
||||
'resourceInternalId' => $resource->getInternalId(),
|
||||
'resourceId' => $resource->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'trigger' => 'http', // http / schedule / event
|
||||
'status' => 'processing', // waiting / processing / completed / failed
|
||||
'responseStatusCode' => 0,
|
||||
'responseHeaders' => [],
|
||||
'requestPath' => $path,
|
||||
@@ -282,13 +333,25 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
'errors' => '',
|
||||
'logs' => '',
|
||||
'duration' => 0.0,
|
||||
'search' => implode(' ', [$functionId, $executionId]),
|
||||
]);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executionId', $execution->getId())
|
||||
->setContext('function', $function);
|
||||
if ($type === 'function') {
|
||||
$execution->setAttribute('resourceType', 'functions');
|
||||
$execution->setAttribute('trigger', 'http'); // http / schedule / event
|
||||
$execution->setAttribute('status', 'processing'); // waiting / processing / completed / failed
|
||||
|
||||
$queueForEvents
|
||||
->setParam('functionId', $resource->getId())
|
||||
->setParam('executionId', $execution->getId())
|
||||
->setContext('function', $resource);
|
||||
} elseif ($type === 'site') {
|
||||
$execution->setAttribute('resourceType', 'sites');
|
||||
|
||||
$queueForEvents
|
||||
->setParam('siteId', $resource->getId())
|
||||
->setParam('executionId', $execution->getId())
|
||||
->setContext('site', $resource);
|
||||
}
|
||||
|
||||
$durationStart = \microtime(true);
|
||||
|
||||
@@ -305,12 +368,12 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
|
||||
// Shared vars
|
||||
foreach ($function->getAttribute('varsProject', []) as $var) {
|
||||
foreach ($resource->getAttribute('varsProject', []) as $var) {
|
||||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
// Function vars
|
||||
foreach ($function->getAttribute('vars', []) as $var) {
|
||||
foreach ($resource->getAttribute('vars', []) as $var) {
|
||||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
@@ -321,14 +384,14 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
// Appwrite vars
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_FUNCTION_ID' => $functionId,
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_ID' => $resource->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
|
||||
@@ -346,31 +409,93 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment->getAttribute('providerRootDirectory', ''),
|
||||
]);
|
||||
|
||||
// SPA fallbackFile override
|
||||
if ($resource->getAttribute('adapter', '') === 'static' && $resource->getAttribute('fallbackFile', '') !== '') {
|
||||
$vars['OPEN_RUNTIMES_STATIC_FALLBACK'] = $resource->getAttribute('fallbackFile', '');
|
||||
}
|
||||
|
||||
/** Execute function */
|
||||
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $runtime['startCommand'];
|
||||
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"';
|
||||
$version = match ($type) {
|
||||
'function' => $resource->getAttribute('version', 'v2'),
|
||||
'site' => 'v5',
|
||||
};
|
||||
$entrypoint = match ($type) {
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
};
|
||||
|
||||
if ($type === 'function') {
|
||||
$runtimeEntrypoint = match ($version) {
|
||||
'v2' => '',
|
||||
default => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $runtime['startCommand'] . '"'
|
||||
};
|
||||
} elseif ($type === 'site') {
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
|
||||
$startCommand = $runtime['startCommand'];
|
||||
|
||||
if (!is_null($framework)) {
|
||||
$adapter = ($framework['adapters'] ?? [])[$resource->getAttribute('adapter', '')] ?? null;
|
||||
if (!is_null($adapter) && isset($adapter['startCommand'])) {
|
||||
$startCommand = $adapter['startCommand'];
|
||||
}
|
||||
}
|
||||
|
||||
$runtimeEntrypoint = 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"';
|
||||
}
|
||||
|
||||
$entrypoint = match ($type) {
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
};
|
||||
|
||||
$executionResponse = $executor->createExecution(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
body: \strlen($body) > 0 ? $body : null,
|
||||
variables: $vars,
|
||||
timeout: $function->getAttribute('timeout', 0),
|
||||
timeout: $resource->getAttribute('timeout', 30),
|
||||
image: $runtime['image'],
|
||||
source: $build->getAttribute('path', ''),
|
||||
entrypoint: $deployment->getAttribute('entrypoint', ''),
|
||||
source: $deployment->getAttribute('buildPath', ''),
|
||||
entrypoint: $entrypoint,
|
||||
version: $version,
|
||||
path: $path,
|
||||
method: $method,
|
||||
headers: $headers,
|
||||
runtimeEntrypoint: $command,
|
||||
cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT,
|
||||
logging: $function->getAttribute('logging', true),
|
||||
runtimeEntrypoint: $runtimeEntrypoint,
|
||||
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
logging: $resource->getAttribute('logging', true),
|
||||
requestTimeout: 30
|
||||
);
|
||||
|
||||
// Branded 404 override
|
||||
if ($executionResponse['statusCode'] === 404 && $resource->getAttribute('adapter', '') === 'static') {
|
||||
$layout = new View(__DIR__ . '/../views/general/404.phtml');
|
||||
$executionResponse['body'] = $layout->render();
|
||||
$executionResponse['headers']['content-length'] = \strlen($executionResponse['body']);
|
||||
}
|
||||
|
||||
// Branded banner for previews
|
||||
if (\is_null($apiKey) || $apiKey->isBannerDisabled() === false) {
|
||||
$transformation = new Transformation();
|
||||
$transformation->addAdapter(new Preview());
|
||||
$transformation->setInput($executionResponse['body']);
|
||||
$transformation->setTraits($executionResponse['headers']);
|
||||
if ($isPreview && $transformation->transform()) {
|
||||
$executionResponse['body'] = $transformation->getOutput();
|
||||
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$headersFiltered = [];
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) {
|
||||
@@ -381,31 +506,38 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
/** Update execution status */
|
||||
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
|
||||
$execution->setAttribute('status', $status);
|
||||
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
|
||||
$execution->setAttribute('responseHeaders', $headersFiltered);
|
||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
|
||||
$execution->setAttribute('responseHeaders', $headersFiltered);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
|
||||
$execution
|
||||
->setAttribute('duration', $durationEnd - $durationStart)
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('responseStatusCode', 500)
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
->setAttribute('responseStatusCode', 500);
|
||||
|
||||
if ($type === 'function') {
|
||||
$execution
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
}
|
||||
Console::error($th->getMessage());
|
||||
|
||||
if ($th instanceof AppwriteException) {
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
$queueForFunctions
|
||||
->setType(Func::TYPE_ASYNC_WRITE)
|
||||
->setExecution($execution)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
if ($type === 'function') {
|
||||
$queueForFunctions
|
||||
->setType(Func::TYPE_ASYNC_WRITE)
|
||||
->setExecution($execution)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
} elseif ($type === 'site') { // TODO: Move it to logs worker later
|
||||
$dbForProject->createDocument('executions', $execution);
|
||||
}
|
||||
}
|
||||
|
||||
$execution->setAttribute('logs', '');
|
||||
@@ -427,7 +559,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$contentType = $header['value'];
|
||||
}
|
||||
|
||||
$response->setHeader($header['name'], $header['value']);
|
||||
if (\strtolower($header['name']) === 'transfer-encoding') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$response->addHeader(\strtolower($header['name']), $header['value']);
|
||||
}
|
||||
|
||||
$response
|
||||
@@ -446,19 +582,28 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
|
||||
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
|
||||
->addMetric(METRIC_EXECUTIONS, 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $resource->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $resource->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $resource->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
->setProject($project)
|
||||
->trigger()
|
||||
;
|
||||
->trigger();
|
||||
|
||||
return true;
|
||||
} elseif ($type === 'api') {
|
||||
$utopia->getRoute()?->label('error', '');
|
||||
return false;
|
||||
} elseif ($type === 'redirect') {
|
||||
$url = $rule->getAttribute('redirectUrl', '');
|
||||
|
||||
$query = ($swooleRequest->server['query_string'] ?? '');
|
||||
if (!empty($query)) {
|
||||
$url .= '?' . $query;
|
||||
}
|
||||
|
||||
$response->redirect($url, \intval($rule->getAttribute('redirectStatusCode', 301)));
|
||||
return true;
|
||||
} else {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type);
|
||||
}
|
||||
@@ -467,7 +612,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
App::init()
|
||||
->groups(['api'])
|
||||
->inject('project')
|
||||
@@ -477,10 +621,9 @@ App::init()
|
||||
throw new AppwriteException(AppwriteException::GENERAL_BAD_REQUEST, 'Admin mode is not allowed for console project');
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
App::init()
|
||||
->groups(['database', 'functions', 'storage', 'messaging'])
|
||||
->groups(['database', 'functions', 'sites', 'messaging'])
|
||||
->inject('project')
|
||||
->inject('request')
|
||||
->action(function (Document $project, Request $request) {
|
||||
@@ -510,10 +653,10 @@ App::init()
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForCertificates')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname) {
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -521,8 +664,8 @@ App::init()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
return;
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,6 +692,9 @@ App::init()
|
||||
if (version_compare($requestFormat, '1.6.0', '<')) {
|
||||
$request->addFilter(new RequestV18());
|
||||
}
|
||||
if (version_compare($requestFormat, '1.7.0', '<')) {
|
||||
$request->addFilter(new RequestV19());
|
||||
}
|
||||
}
|
||||
|
||||
$domain = $request->getHostname();
|
||||
@@ -590,23 +736,17 @@ App::init()
|
||||
]);
|
||||
}
|
||||
|
||||
$owner = '';
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
|
||||
$owner = 'Appwrite';
|
||||
}
|
||||
|
||||
if ($domainDocument->isEmpty()) {
|
||||
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique();
|
||||
$domainDocument = new Document([
|
||||
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
|
||||
'$id' => System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique(),
|
||||
'$id' => $ruleId,
|
||||
'domain' => $domain->get(),
|
||||
'resourceType' => 'api',
|
||||
'type' => 'api',
|
||||
'status' => 'verifying',
|
||||
'projectId' => $console->getId(),
|
||||
'projectInternalId' => $console->getInternalId(),
|
||||
'owner' => $owner,
|
||||
'region' => $console->getAttribute('region')
|
||||
'projectId' => 'console',
|
||||
'projectInternalId' => 'console',
|
||||
'search' => implode(' ', [$ruleId, $domain->get()]),
|
||||
]);
|
||||
|
||||
$domainDocument = $dbForPlatform->createDocument('rules', $domainDocument);
|
||||
@@ -640,6 +780,22 @@ App::init()
|
||||
$validator = new Hostname($clients);
|
||||
if ($validator->isValid($origin)) {
|
||||
$refDomainOrigin = $origin;
|
||||
} elseif (!empty($origin)) {
|
||||
// Auto-allow domains with linked rule
|
||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
|
||||
} else {
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [$origin]),
|
||||
Query::limit(1)
|
||||
])
|
||||
)[0] ?? new Document();
|
||||
}
|
||||
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) {
|
||||
$refDomainOrigin = $origin;
|
||||
}
|
||||
}
|
||||
|
||||
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
|
||||
@@ -688,8 +844,11 @@ App::init()
|
||||
if (version_compare($responseFormat, '1.6.0', '<')) {
|
||||
$response->addFilter(new ResponseV18());
|
||||
}
|
||||
if (version_compare($responseFormat, '1.7.0', '<')) {
|
||||
$response->addFilter(new ResponseV19());
|
||||
}
|
||||
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
|
||||
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
||||
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,12 +909,12 @@ App::options()
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->inject('project')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project, ?Key $apiKey) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -763,8 +922,8 @@ App::options()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
return;
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,18 +1039,17 @@ App::error()
|
||||
if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) {
|
||||
// Register error logger
|
||||
try {
|
||||
$loggingProvider = new DSN($providerConfig);
|
||||
$loggingProvider = new DSN($providerConfig ?? '');
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
|
||||
if (!empty($providerName) && $providerName === 'sentry') {
|
||||
$key = $loggingProvider->getPassword();
|
||||
$projectId = $loggingProvider->getUser() ?? '';
|
||||
$host = 'https://' . $loggingProvider->getHost();
|
||||
$sampleRate = $loggingProvider->getParam('sample', 0.01);
|
||||
|
||||
$adapter = new Sentry($projectId, $key, $host);
|
||||
$logger = new Logger($adapter);
|
||||
$logger->setSample($sampleRate);
|
||||
$logger->setSample(0.01);
|
||||
$publish = true;
|
||||
} else {
|
||||
throw new \Exception('Invalid experimental logging provider');
|
||||
@@ -1050,6 +1208,7 @@ App::error()
|
||||
->setParam('trace', $output['trace'] ?? []);
|
||||
|
||||
$response->html($layout->render());
|
||||
return;
|
||||
}
|
||||
|
||||
$response->dynamic(
|
||||
@@ -1071,11 +1230,11 @@ App::get('/robots.txt')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -1083,7 +1242,9 @@ App::get('/robots.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1100,11 +1261,11 @@ App::get('/humans.txt')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -1112,7 +1273,9 @@ App::get('/humans.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1174,6 +1337,21 @@ App::get('/v1/ping')
|
||||
->desc('Test the connection between the Appwrite and the SDK.')
|
||||
->label('scope', 'global')
|
||||
->label('event', 'projects.[projectId].ping')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'ping',
|
||||
name: 'get',
|
||||
hide: true,
|
||||
description: <<<EOT
|
||||
Send a ping to project as part of onboarding.
|
||||
EOT,
|
||||
auth: [],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_ANY,
|
||||
)
|
||||
],
|
||||
))
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
@@ -1201,6 +1379,34 @@ App::get('/v1/ping')
|
||||
$response->text('Pong!');
|
||||
});
|
||||
|
||||
// Preview authorization
|
||||
App::get('/_appwrite/authorize')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('previewHostname')
|
||||
->action(function (Request $request, Response $response, string $previewHostname) {
|
||||
|
||||
$host = $request->getHostname() ?? '';
|
||||
if (!empty($previewHostname)) {
|
||||
$host = $previewHostname;
|
||||
}
|
||||
|
||||
$referrer = $request->getReferer();
|
||||
$protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME);
|
||||
|
||||
$jwt = $request->getParam('jwt', '');
|
||||
$path = $request->getParam('path', '');
|
||||
|
||||
$duration = 60 * 60 * 24; // 1 day in seconds
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieNamePreview, $jwt, (new \DateTime($expire))->getTimestamp(), '/', $host, ('https' === $protocol), true, null)
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($protocol . '://' . $host . $path);
|
||||
});
|
||||
|
||||
App::wildcard()
|
||||
->groups(['api'])
|
||||
->label('scope', 'global')
|
||||
@@ -1209,9 +1415,15 @@ App::wildcard()
|
||||
});
|
||||
|
||||
foreach (Config::getParam('services', []) as $service) {
|
||||
include_once $service['controller'];
|
||||
if (!empty($service['controller'])) {
|
||||
include_once $service['controller'];
|
||||
}
|
||||
}
|
||||
|
||||
// Modules
|
||||
$platform = new Appwrite();
|
||||
$platform->init(Service::TYPE_HTTP);
|
||||
|
||||
// Check for any errors found while we were initialising the SDK Methods.
|
||||
if (!empty(Method::getErrors())) {
|
||||
throw new \Exception('Errors found during SDK initialization:' . PHP_EOL . implode(PHP_EOL, Method::getErrors()));
|
||||
|
||||
@@ -92,20 +92,8 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
|
||||
|
||||
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
|
||||
$value = 1;
|
||||
|
||||
switch ($event) {
|
||||
case Database::EVENT_DOCUMENT_DELETE:
|
||||
$value = -1;
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_DELETE:
|
||||
$value = -1 * $document->getAttribute('modified', 0);
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_CREATE:
|
||||
$value = $document->getAttribute('modified', 0);
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_UPSERT:
|
||||
$value = $document->getAttribute('created', 0);
|
||||
break;
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$value = -1;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
@@ -169,6 +157,15 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
|
||||
$queueForStatsUsage
|
||||
->addMetric(METRIC_FUNCTIONS, $value); // per project
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForStatsUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
case $document->getCollection() === 'sites':
|
||||
$queueForStatsUsage
|
||||
->addMetric(METRIC_SITES, $value); // per project
|
||||
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForStatsUsage
|
||||
->addReduce($document);
|
||||
@@ -250,36 +247,34 @@ App::init()
|
||||
subject: 'keys'
|
||||
);
|
||||
|
||||
if (!$dbKey) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
if ($dbKey) {
|
||||
$accessedAt = $dbKey->getAttribute('accessedAt', '');
|
||||
|
||||
$accessedAt = $dbKey->getAttribute('accessedAt', '');
|
||||
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||
$dbKey->setAttribute('accessedAt', DateTime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
|
||||
|
||||
if ($sdkValidator->isValid($sdk)) {
|
||||
$sdks = $dbKey->getAttribute('sdks', []);
|
||||
|
||||
if (!in_array($sdk, $sdks)) {
|
||||
$sdks[] = $sdk;
|
||||
$dbKey->setAttribute('sdks', $sdks);
|
||||
|
||||
/** Update access time as well */
|
||||
$dbKey->setAttribute('accessedAt', Datetime::now());
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||
$dbKey->setAttribute('accessedAt', DateTime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
}
|
||||
|
||||
$queueForAudits->setUser($user);
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
|
||||
|
||||
if ($sdkValidator->isValid($sdk)) {
|
||||
$sdks = $dbKey->getAttribute('sdks', []);
|
||||
|
||||
if (!in_array($sdk, $sdks)) {
|
||||
$sdks[] = $sdk;
|
||||
$dbKey->setAttribute('sdks', $sdks);
|
||||
|
||||
/** Update access time as well */
|
||||
$dbKey->setAttribute('accessedAt', Datetime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
}
|
||||
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
}
|
||||
} // Admin User Authentication
|
||||
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
|
||||
@@ -340,8 +335,6 @@ App::init()
|
||||
*/
|
||||
$method = $route->getLabel('sdk', false);
|
||||
|
||||
// Take the first method if there's more than one,
|
||||
// namespace can not differ between methods on the same route
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
@@ -406,8 +399,7 @@ App::init()
|
||||
->inject('timelimit')
|
||||
->inject('mode')
|
||||
->inject('apiKey')
|
||||
->inject('plan')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey, array $plan) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -525,9 +517,6 @@ App::init()
|
||||
$dbForProject
|
||||
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
|
||||
$project,
|
||||
$document,
|
||||
@@ -540,10 +529,6 @@ App::init()
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
if ($useCache) {
|
||||
$route = $utopia->match($request);
|
||||
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
|
||||
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$cache = new Cache(
|
||||
@@ -553,10 +538,10 @@ App::init()
|
||||
$data = $cache->load($key, $timestamp);
|
||||
|
||||
if (!empty($data) && !$cacheLog->isEmpty()) {
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
|
||||
$type = $parts[0] ?? null;
|
||||
|
||||
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
|
||||
if ($type === 'bucket') {
|
||||
$bucketId = $parts[1] ?? null;
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
@@ -597,10 +582,8 @@ App::init()
|
||||
$response
|
||||
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->setContentType($cacheLog->getAttribute('mimeType'));
|
||||
if (!$isImageTransformation || !$isDisabled) {
|
||||
$response->send($data);
|
||||
}
|
||||
->setContentType($cacheLog->getAttribute('mimeType'))
|
||||
->send($data);
|
||||
} else {
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
|
||||
+137
-94
@@ -13,6 +13,7 @@ use Swoole\Table;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Compression\Compression;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -28,6 +29,8 @@ use Utopia\Pools\Group;
|
||||
use Utopia\Swoole\Files;
|
||||
use Utopia\System\System;
|
||||
|
||||
Files::load(__DIR__.'/../public');
|
||||
|
||||
const DOMAIN_SYNC_TIMER = 30; // 30 seconds
|
||||
|
||||
$domains = new Table(1_000_000); // 1 million rows
|
||||
@@ -43,6 +46,29 @@ $http = new Server(
|
||||
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
||||
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$http
|
||||
->set([
|
||||
'worker_num' => $totalWorkers,
|
||||
'dispatch_func' => 'dispatch',
|
||||
'open_http2_protocol' => true,
|
||||
'http_compression' => false,
|
||||
'package_max_length' => $payloadSize,
|
||||
'buffer_output_size' => $payloadSize,
|
||||
'task_worker_num' => 1, // required for the task to fetch domains background
|
||||
]);
|
||||
|
||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
/**
|
||||
* Assigns HTTP requests to worker threads by analyzing its payload/content.
|
||||
*
|
||||
@@ -60,105 +86,76 @@ $totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intva
|
||||
*/
|
||||
function dispatch(Server $server, int $fd, int $type, $data = null): int
|
||||
{
|
||||
$resolveWorkerId = function (Server $server, $data = null) {
|
||||
global $totalWorkers, $domains;
|
||||
global $totalWorkers, $domains;
|
||||
|
||||
// If data is not set we can send request to any worker
|
||||
// first we try to pick idle worker, if not we randomly pick a worker
|
||||
if ($data === null) {
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return rand(0, $totalWorkers - 1);
|
||||
}
|
||||
|
||||
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
|
||||
|
||||
// Each worker has numeric ID, starting from 0 and incrementing
|
||||
// From 0 to riskyWorkers, we consider safe workers
|
||||
// From riskyWorkers to totalWorkers, we consider risky workers
|
||||
$riskyWorkers = (int)floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
|
||||
|
||||
$domain = '';
|
||||
// max up to 3 as first line has request details and second line has host
|
||||
$lines = explode("\n", $data, 3);
|
||||
$request = $lines[0];
|
||||
if (count($lines) > 1) {
|
||||
$domain = trim(explode('Host: ', $lines[1])[1]);
|
||||
}
|
||||
|
||||
// Sync executions are considered risky
|
||||
$risky = false;
|
||||
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
|
||||
$risky = true;
|
||||
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
|
||||
$risky = true;
|
||||
} elseif ($domains->get(md5($domain), 'value') === 1) {
|
||||
// executions request coming from custom domain
|
||||
$risky = true;
|
||||
}
|
||||
|
||||
if ($risky) {
|
||||
// If risky request, only consider risky workers
|
||||
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
|
||||
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
|
||||
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
|
||||
// If idle worker found, give to him
|
||||
return $j;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle workers, give to random risky worker
|
||||
$worker = rand($riskyWorkers, $totalWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
}
|
||||
|
||||
// If safe request, give to any idle worker
|
||||
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
|
||||
// If data is not set we can send request to any worker
|
||||
// first we try to pick idle worker, if not we randomly pick a worker
|
||||
if ($data === null) {
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return rand(0, $totalWorkers - 1);
|
||||
}
|
||||
|
||||
// If no idle worker found, give to random safe worker
|
||||
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
|
||||
$worker = rand(0, $riskyWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
|
||||
|
||||
// Each worker has numeric ID, starting from 0 and incrementing
|
||||
// From 0 to riskyWorkers, we consider safe workers
|
||||
// From riskyWorkers to totalWorkers, we consider risky workers
|
||||
$riskyWorkers = (int) floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
|
||||
|
||||
$domain = '';
|
||||
// max up to 3 as first line has request details and second line has host
|
||||
$lines = explode("\n", $data, 3);
|
||||
$request = $lines[0];
|
||||
if (count($lines) > 1) {
|
||||
$domain = trim(explode('Host: ', $lines[1])[1]);
|
||||
}
|
||||
|
||||
// Sync executions are considered risky
|
||||
$risky = false;
|
||||
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
|
||||
$risky = true;
|
||||
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
|
||||
$risky = true;
|
||||
} elseif ($domains->get(md5($domain), 'value') === 1) {
|
||||
// executions request coming from custom domain
|
||||
$risky = true;
|
||||
}
|
||||
|
||||
if ($risky) {
|
||||
// If risky request, only consider risky workers
|
||||
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
|
||||
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
|
||||
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
|
||||
// If idle worker found, give to him
|
||||
return $j;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle workers, give to random risky worker
|
||||
$worker = rand($riskyWorkers, $totalWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
};
|
||||
$workerId = $resolveWorkerId($server, $data);
|
||||
$server->bind($fd, $workerId);
|
||||
return $workerId;
|
||||
}
|
||||
|
||||
// If safe request, give to any idle worker
|
||||
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle worker found, give to random safe worker
|
||||
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
|
||||
$worker = rand(0, $riskyWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
}
|
||||
|
||||
|
||||
$http
|
||||
->set([
|
||||
Constant::OPTION_WORKER_NUM => $totalWorkers,
|
||||
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
|
||||
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
|
||||
Constant::OPTION_HTTP_COMPRESSION => false,
|
||||
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
|
||||
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
|
||||
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
|
||||
]);
|
||||
|
||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
|
||||
@@ -255,8 +252,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() &&
|
||||
!$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) {
|
||||
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty()) {
|
||||
Console::info(" └── Creating default bucket...");
|
||||
$dbForPlatform->createDocument('buckets', new Document([
|
||||
'$id' => ID::custom('default'),
|
||||
@@ -308,6 +304,54 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
|
||||
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
}
|
||||
|
||||
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
|
||||
Console::info(" └── Creating screenshots bucket...");
|
||||
Authorization::skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
|
||||
'$id' => ID::custom('screenshots'),
|
||||
'$collection' => ID::custom('buckets'),
|
||||
'name' => 'Screenshots',
|
||||
'maximumFileSize' => 5000000, // ~5MB
|
||||
'allowedFileExtensions' => [ 'png' ],
|
||||
'enabled' => true,
|
||||
'compression' => Compression::GZIP,
|
||||
'encryption' => false,
|
||||
'antivirus' => false,
|
||||
'fileSecurity' => true,
|
||||
'$permissions' => [],
|
||||
'search' => 'buckets Screenshots',
|
||||
])));
|
||||
|
||||
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
|
||||
|
||||
Console::info(" └── Creating files collection for screenshots bucket...");
|
||||
$files = $collections['buckets']['files'] ?? [];
|
||||
if (empty($files)) {
|
||||
throw new Exception('Files collection is not configured.');
|
||||
}
|
||||
|
||||
$attributes = array_map(fn ($attr) => new Document([
|
||||
'$id' => ID::custom($attr['$id']),
|
||||
'type' => $attr['type'],
|
||||
'size' => $attr['size'],
|
||||
'required' => $attr['required'],
|
||||
'signed' => $attr['signed'],
|
||||
'array' => $attr['array'],
|
||||
'filters' => $attr['filters'],
|
||||
'default' => $attr['default'] ?? null,
|
||||
'format' => $attr['format'] ?? ''
|
||||
]), $files['attributes']);
|
||||
|
||||
$indexes = array_map(fn ($index) => new Document([
|
||||
'$id' => ID::custom($index['$id']),
|
||||
'type' => $index['type'],
|
||||
'attributes' => $index['attributes'],
|
||||
'lengths' => $index['lengths'],
|
||||
'orders' => $index['orders'],
|
||||
]), $files['indexes']);
|
||||
|
||||
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes));
|
||||
}
|
||||
});
|
||||
|
||||
$projectCollections = $collections['projects'];
|
||||
@@ -496,7 +540,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
});
|
||||
|
||||
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
|
||||
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
|
||||
$http->on('Task', function () use ($register, $domains) {
|
||||
$lastSyncUpdate = null;
|
||||
$pools = $register->get('pools');
|
||||
App::setResource('pools', fn () => $pools);
|
||||
@@ -520,7 +564,6 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
|
||||
if ($lastSyncUpdate != null) {
|
||||
$queries[] = Query::greaterThanEqual('$updatedAt', $lastSyncUpdate);
|
||||
}
|
||||
$queries[] = Query::equal('resourceType', ['function']);
|
||||
$results = [];
|
||||
try {
|
||||
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
|
||||
@@ -531,7 +574,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
|
||||
$sum = count($results);
|
||||
foreach ($results as $document) {
|
||||
$domain = $document->getAttribute('domain');
|
||||
if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
|
||||
if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS')) || str_ends_with($domain, System::getEnv('_APP_DOMAIN_SITES'))) {
|
||||
continue;
|
||||
}
|
||||
$domains->set(md5($domain), ['value' => 1]);
|
||||
|
||||
+1925
-9
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
Config::load('events', __DIR__ . '/../config/events.php');
|
||||
Config::load('auth', __DIR__ . '/../config/auth.php');
|
||||
Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs
|
||||
Config::load('errors', __DIR__ . '/../config/errors.php');
|
||||
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
|
||||
Config::load('platforms', __DIR__ . '/../config/platforms.php');
|
||||
Config::load('console', __DIR__ . '/../config/console.php');
|
||||
Config::load('collections', __DIR__ . '/../config/collections.php');
|
||||
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
|
||||
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
|
||||
Config::load('usage', __DIR__ . '/../config/usage.php');
|
||||
Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/../config/services.php'); // List of services
|
||||
Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables
|
||||
Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions
|
||||
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php');
|
||||
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php');
|
||||
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php');
|
||||
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php');
|
||||
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php');
|
||||
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php');
|
||||
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php');
|
||||
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php');
|
||||
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php');
|
||||
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php');
|
||||
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php');
|
||||
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
|
||||
Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php');
|
||||
Config::load('function-templates', __DIR__ . '/../config/function-templates.php');
|
||||
@@ -1,240 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Functions\Specification;
|
||||
|
||||
const APP_NAME = 'Appwrite';
|
||||
const APP_DOMAIN = 'appwrite.io';
|
||||
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
|
||||
const APP_EMAIL_SECURITY = ''; // Default security email address
|
||||
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
|
||||
const APP_MODE_DEFAULT = 'default';
|
||||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 12;
|
||||
const APP_LIMIT_COUNT = 5000;
|
||||
const APP_LIMIT_USERS = 10_000;
|
||||
const APP_LIMIT_USER_PASSWORD_HISTORY = 20;
|
||||
const APP_LIMIT_USER_SESSIONS_MAX = 100;
|
||||
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
|
||||
const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
|
||||
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
|
||||
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
|
||||
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
||||
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
|
||||
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
|
||||
const APP_LIMIT_SUBQUERY = 1000;
|
||||
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
|
||||
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
||||
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||
const APP_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations
|
||||
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4318;
|
||||
const APP_VERSION_STABLE = '1.6.2';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
|
||||
const APP_DATABASE_ATTRIBUTE_URL = 'url';
|
||||
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
|
||||
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
|
||||
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_QUERY_MAX_VALUES = 500;
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
const APP_STORAGE_BUILDS = '/storage/builds';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
|
||||
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
|
||||
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
|
||||
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
|
||||
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
|
||||
const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
|
||||
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
|
||||
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
const APP_HOSTNAME_INTERNAL = 'appwrite';
|
||||
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
|
||||
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
|
||||
const APP_FUNCTION_MEMORY_DEFAULT = 512;
|
||||
const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
|
||||
// Database Worker Types
|
||||
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
|
||||
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
|
||||
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
|
||||
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
|
||||
const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection';
|
||||
const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase';
|
||||
|
||||
// Build Worker Types
|
||||
const BUILD_TYPE_DEPLOYMENT = 'deployment';
|
||||
const BUILD_TYPE_RETRY = 'retry';
|
||||
|
||||
// Deletion Types
|
||||
const DELETE_TYPE_DATABASES = 'databases';
|
||||
const DELETE_TYPE_DOCUMENT = 'document';
|
||||
const DELETE_TYPE_COLLECTIONS = 'collections';
|
||||
const DELETE_TYPE_PROJECTS = 'projects';
|
||||
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
|
||||
const DELETE_TYPE_USERS = 'users';
|
||||
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
|
||||
const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||
const DELETE_TYPE_AUDIT = 'audit';
|
||||
const DELETE_TYPE_ABUSE = 'abuse';
|
||||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
const DELETE_TYPE_INSTALLATIONS = 'installations';
|
||||
const DELETE_TYPE_RULES = 'rules';
|
||||
const DELETE_TYPE_SESSIONS = 'sessions';
|
||||
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
||||
const DELETE_TYPE_SCHEDULES = 'schedules';
|
||||
const DELETE_TYPE_TOPIC = 'topic';
|
||||
const DELETE_TYPE_TARGET = 'target';
|
||||
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
||||
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||
const DELETE_TYPE_MAINTENANCE = 'maintenance';
|
||||
|
||||
// Message types
|
||||
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
|
||||
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
|
||||
// Mail Types
|
||||
const MAIL_TYPE_VERIFICATION = 'verification';
|
||||
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
|
||||
const MAIL_TYPE_RECOVERY = 'recovery';
|
||||
const MAIL_TYPE_INVITATION = 'invitation';
|
||||
const MAIL_TYPE_CERTIFICATE = 'certificate';
|
||||
// Auth Types
|
||||
const APP_AUTH_TYPE_SESSION = 'Session';
|
||||
const APP_AUTH_TYPE_JWT = 'JWT';
|
||||
const APP_AUTH_TYPE_KEY = 'Key';
|
||||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
// Function headers
|
||||
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
||||
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||
// Message types
|
||||
const MESSAGE_TYPE_EMAIL = 'email';
|
||||
const MESSAGE_TYPE_SMS = 'sms';
|
||||
const MESSAGE_TYPE_PUSH = 'push';
|
||||
// API key types
|
||||
const API_KEY_STANDARD = 'standard';
|
||||
const API_KEY_DYNAMIC = 'dynamic';
|
||||
// Usage metrics
|
||||
const METRIC_TEAMS = 'teams';
|
||||
const METRIC_USERS = 'users';
|
||||
const METRIC_WEBHOOKS_SENT = 'webhooks.events.sent';
|
||||
const METRIC_WEBHOOKS_FAILED = 'webhooks.events.failed';
|
||||
const METRIC_WEBHOOK_ID_SENT = '{webhookInternalId}.webhooks.events.sent';
|
||||
const METRIC_WEBHOOK_ID_FAILED = '{webhookInternalId}.webhooks.events.failed';
|
||||
const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone';
|
||||
const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}';
|
||||
const METRIC_MESSAGES = 'messages';
|
||||
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
|
||||
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
|
||||
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
|
||||
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
|
||||
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
|
||||
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
|
||||
const METRIC_SESSIONS = 'sessions';
|
||||
const METRIC_DATABASES = 'databases';
|
||||
const METRIC_COLLECTIONS = 'collections';
|
||||
const METRIC_DATABASES_STORAGE = 'databases.storage';
|
||||
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
|
||||
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
|
||||
const METRIC_DOCUMENTS = 'documents';
|
||||
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
|
||||
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
|
||||
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
|
||||
const METRIC_DATABASES_OPERATIONS_READS = 'databases.operations.reads';
|
||||
const METRIC_DATABASE_ID_OPERATIONS_READS = '{databaseInternalId}.databases.operations.reads';
|
||||
const METRIC_DATABASES_OPERATIONS_WRITES = 'databases.operations.writes';
|
||||
const METRIC_DATABASE_ID_OPERATIONS_WRITES = '{databaseInternalId}.databases.operations.writes';
|
||||
const METRIC_BUCKETS = 'buckets';
|
||||
const METRIC_FILES = 'files';
|
||||
const METRIC_FILES_STORAGE = 'files.storage';
|
||||
const METRIC_FILES_TRANSFORMATIONS = 'files.transformations';
|
||||
const METRIC_BUCKET_ID_FILES_TRANSFORMATIONS = '{bucketInternalId}.files.transformations';
|
||||
const METRIC_FILES_IMAGES_TRANSFORMED = 'files.imagesTransformed';
|
||||
const METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED = '{bucketInternalId}.files.imagesTransformed';
|
||||
const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files';
|
||||
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
|
||||
const METRIC_FUNCTIONS = 'functions';
|
||||
const METRIC_DEPLOYMENTS = 'deployments';
|
||||
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
|
||||
const METRIC_BUILDS = 'builds';
|
||||
const METRIC_BUILDS_SUCCESS = 'builds.success';
|
||||
const METRIC_BUILDS_FAILED = 'builds.failed';
|
||||
const METRIC_BUILDS_STORAGE = 'builds.storage';
|
||||
const METRIC_BUILDS_COMPUTE = 'builds.compute';
|
||||
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
|
||||
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
|
||||
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
|
||||
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
|
||||
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
|
||||
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
|
||||
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
|
||||
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
|
||||
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
|
||||
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
|
||||
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
|
||||
const METRIC_EXECUTIONS = 'executions';
|
||||
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
|
||||
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
|
||||
const METRIC_NETWORK_REQUESTS = 'network.requests';
|
||||
const METRIC_NETWORK_INBOUND = 'network.inbound';
|
||||
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
|
||||
const METRIC_MAU = 'users.mau';
|
||||
const METRIC_DAU = 'users.dau';
|
||||
const METRIC_WAU = 'users.wau';
|
||||
const METRIC_WEBHOOKS = 'webhooks';
|
||||
const METRIC_PLATFORMS = 'platforms';
|
||||
const METRIC_PROVIDERS = 'providers';
|
||||
const METRIC_TOPICS = 'topics';
|
||||
const METRIC_TARGETS = 'targets';
|
||||
const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets';
|
||||
const METRIC_KEYS = 'keys';
|
||||
const METRIC_DOMAINS = 'domains';
|
||||
const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds';
|
||||
const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage';
|
||||
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
|
||||
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
|
||||
|
||||
// Resource types
|
||||
|
||||
const RESOURCE_TYPE_PROJECTS = 'projects';
|
||||
const RESOURCE_TYPE_FUNCTIONS = 'functions';
|
||||
const RESOURCE_TYPE_DATABASES = 'databases';
|
||||
const RESOURCE_TYPE_BUCKETS = 'buckets';
|
||||
const RESOURCE_TYPE_PROVIDERS = 'providers';
|
||||
const RESOURCE_TYPE_TOPICS = 'topics';
|
||||
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
|
||||
const RESOURCE_TYPE_MESSAGES = 'messages';
|
||||
@@ -1,404 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\System\System;
|
||||
|
||||
Database::addFilter(
|
||||
'casting',
|
||||
function (mixed $value) {
|
||||
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
|
||||
},
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return json_decode($value, true)['value'];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'enum',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('elements')) {
|
||||
$attribute->removeAttribute('elements');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['elements'])) {
|
||||
$attribute->setAttribute('elements', $formatOptions['elements']);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'range',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('min')) {
|
||||
$attribute->removeAttribute('min');
|
||||
}
|
||||
if ($attribute->isSet('max')) {
|
||||
$attribute->removeAttribute('max');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
|
||||
$attribute
|
||||
->setAttribute('min', $formatOptions['min'])
|
||||
->setAttribute('max', $formatOptions['max']);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryAttributes',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
$attributes = $database->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForAttributes()),
|
||||
]);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
|
||||
$options = $attribute->getAttribute('options');
|
||||
foreach ($options as $key => $value) {
|
||||
$attribute->setAttribute($key, $value);
|
||||
}
|
||||
$attribute->removeAttribute('options');
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryIndexes',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('indexes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForIndexes()),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryPlatforms',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('platforms', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryKeys',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('keys', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryWebhooks',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('webhooks', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQuerySessions',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database->find('sessions', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTokens',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('tokens', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryChallenges',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('challenges', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryAuthenticators',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('authenticators', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryMemberships',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('memberships', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryVariables',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('variables', [
|
||||
Query::equal('resourceInternalId', [$document->getInternalId()]),
|
||||
Query::equal('resourceType', ['function']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'encrypt',
|
||||
function (mixed $value) {
|
||||
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$tag = null;
|
||||
|
||||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'iv' => \bin2hex($iv),
|
||||
'tag' => \bin2hex($tag ?? ''),
|
||||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return;
|
||||
}
|
||||
$value = json_decode($value, true);
|
||||
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryProjectVariables',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('variables', [
|
||||
Query::equal('resourceType', ['project']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'userSearch',
|
||||
function (mixed $value, Document $user) {
|
||||
$searchValues = [
|
||||
$user->getId(),
|
||||
$user->getAttribute('email', ''),
|
||||
$user->getAttribute('name', ''),
|
||||
$user->getAttribute('phone', '')
|
||||
];
|
||||
|
||||
foreach ($user->getAttribute('labels', []) as $label) {
|
||||
$searchValues[] = 'label:' . $label;
|
||||
}
|
||||
|
||||
$search = implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTargets',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('targets', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryTopicTargets',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
$targetIds = Authorization::skip(fn () => \array_map(
|
||||
fn ($document) => $document->getAttribute('targetInternalId'),
|
||||
$database->find('subscribers', [
|
||||
Query::equal('topicInternalId', [$document->getInternalId()]),
|
||||
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
|
||||
])
|
||||
));
|
||||
if (\count($targetIds) > 0) {
|
||||
return $database->skipValidation(fn () => $database->find('targets', [
|
||||
Query::equal('$internalId', $targetIds)
|
||||
]));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'providerSearch',
|
||||
function (mixed $value, Document $provider) {
|
||||
$searchValues = [
|
||||
$provider->getId(),
|
||||
$provider->getAttribute('name', ''),
|
||||
$provider->getAttribute('provider', ''),
|
||||
$provider->getAttribute('type', '')
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'topicSearch',
|
||||
function (mixed $value, Document $topic) {
|
||||
$searchValues = [
|
||||
$topic->getId(),
|
||||
$topic->getAttribute('name', ''),
|
||||
$topic->getAttribute('description', ''),
|
||||
];
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'messageSearch',
|
||||
function (mixed $value, Document $message) {
|
||||
$searchValues = [
|
||||
$message->getId(),
|
||||
$message->getAttribute('description', ''),
|
||||
$message->getAttribute('status', ''),
|
||||
];
|
||||
|
||||
$data = \json_decode($message->getAttribute('data', []), true);
|
||||
$providerType = $message->getAttribute('providerType', '');
|
||||
|
||||
switch ($providerType) {
|
||||
case MESSAGE_TYPE_EMAIL:
|
||||
$searchValues[] = $data['subject'];
|
||||
$searchValues[] = MESSAGE_TYPE_EMAIL;
|
||||
break;
|
||||
case MESSAGE_TYPE_SMS:
|
||||
$searchValues[] = $data['content'];
|
||||
$searchValues[] = MESSAGE_TYPE_SMS;
|
||||
break;
|
||||
case MESSAGE_TYPE_PUSH:
|
||||
$searchValues[] = $data['title'] ?? '';
|
||||
$searchValues[] = MESSAGE_TYPE_PUSH;
|
||||
break;
|
||||
}
|
||||
|
||||
$search = \implode(' ', \array_filter($searchValues));
|
||||
|
||||
return $search;
|
||||
},
|
||||
function (mixed $value) {
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
use Utopia\Validator\IP;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
|
||||
return new Email();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
|
||||
return new DatetimeValidator();
|
||||
}, Database::VAR_DATETIME);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
|
||||
$elements = $attribute['formatOptions']['elements'] ?? [];
|
||||
return new WhiteList($elements, true);
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
|
||||
return new IP();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
|
||||
return new URL();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}, Database::VAR_INTEGER);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_FLOAT);
|
||||
}, Database::VAR_FLOAT);
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Locale\Locale;
|
||||
|
||||
Locale::$exceptions = false;
|
||||
|
||||
$locales = Config::getParam('locale-codes', []);
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$code = $locale['code'];
|
||||
|
||||
$path = __DIR__ . '/../config/locale/translations/' . $code . '.json';
|
||||
|
||||
if (!\file_exists($path)) {
|
||||
$path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
|
||||
if (!\file_exists($path)) {
|
||||
$path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
|
||||
}
|
||||
}
|
||||
|
||||
Locale::setLanguageFromJSON($code, $path);
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Promises\Adapter\Swoole;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\PubSub\Adapter\Redis as PubSub;
|
||||
use Appwrite\URL\URL as AppwriteURL;
|
||||
use MaxMind\Db\Reader;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Swoole\Database\PDOProxy;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Adapter\MySQL;
|
||||
use Utopia\Database\Adapter\SQL;
|
||||
use Utopia\Database\PDO;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Adapter\AppSignal;
|
||||
use Utopia\Logger\Adapter\LogOwl;
|
||||
use Utopia\Logger\Adapter\Raygun;
|
||||
use Utopia\Logger\Adapter\Sentry;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Pools\Pool;
|
||||
use Utopia\Queue;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
|
||||
$register = new Registry();
|
||||
|
||||
App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
||||
|
||||
if (!App::isProduction()) {
|
||||
// Allow specific domains to skip public domain validation in dev environment
|
||||
// Useful for existing tests involving webhooks
|
||||
PublicDomain::allow(['request-catcher']);
|
||||
}
|
||||
$register->set('logger', function () {
|
||||
// Register error logger
|
||||
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if (empty($providerConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$loggingProvider = new DSN($providerConfig ?? '');
|
||||
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
$providerConfig = match ($providerName) {
|
||||
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
|
||||
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
|
||||
default => ['key' => $loggingProvider->getHost()],
|
||||
};
|
||||
} catch (Throwable $th) {
|
||||
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
|
||||
Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage());
|
||||
$configChunks = \explode(";", $providerConfig);
|
||||
|
||||
$providerConfig = match ($providerName) {
|
||||
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
|
||||
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
|
||||
default => ['key' => $providerConfig],
|
||||
};
|
||||
}
|
||||
|
||||
if (empty($providerName) || empty($providerConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Logger::hasProvider($providerName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
|
||||
}
|
||||
|
||||
try {
|
||||
$adapter = match ($providerName) {
|
||||
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
|
||||
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
|
||||
'raygun' => new Raygun($providerConfig['key']),
|
||||
'appsignal' => new AppSignal($providerConfig['key']),
|
||||
default => null
|
||||
};
|
||||
} catch (Throwable $th) {
|
||||
$adapter = null;
|
||||
}
|
||||
|
||||
if ($adapter === null) {
|
||||
Console::error("Logging provider not supported. Logging is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
return new Logger($adapter);
|
||||
});
|
||||
|
||||
$register->set('pools', function () {
|
||||
$group = new Group();
|
||||
|
||||
$fallbackForDB = 'db_main=' . AppwriteURL::unparse([
|
||||
'scheme' => 'mariadb',
|
||||
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
|
||||
'port' => System::getEnv('_APP_DB_PORT', '3306'),
|
||||
'user' => System::getEnv('_APP_DB_USER', ''),
|
||||
'pass' => System::getEnv('_APP_DB_PASS', ''),
|
||||
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
|
||||
]);
|
||||
$fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([
|
||||
'scheme' => 'redis',
|
||||
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
|
||||
'port' => System::getEnv('_APP_REDIS_PORT', '6379'),
|
||||
'user' => System::getEnv('_APP_REDIS_USER', ''),
|
||||
'pass' => System::getEnv('_APP_REDIS_PASS', ''),
|
||||
]);
|
||||
|
||||
$connections = [
|
||||
'console' => [
|
||||
'type' => 'database',
|
||||
'dsns' => $fallbackForDB,
|
||||
'multiple' => false,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'database' => [
|
||||
'type' => 'database',
|
||||
'dsns' => $fallbackForDB,
|
||||
'multiple' => true,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'logs' => [
|
||||
'type' => 'database',
|
||||
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB),
|
||||
'multiple' => false,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'publisher' => [
|
||||
'type' => 'publisher',
|
||||
'dsns' => $fallbackForRedis,
|
||||
'multiple' => false,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
'consumer' => [
|
||||
'type' => 'consumer',
|
||||
'dsns' => $fallbackForRedis,
|
||||
'multiple' => false,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
'pubsub' => [
|
||||
'type' => 'pubsub',
|
||||
'dsns' => $fallbackForRedis,
|
||||
'multiple' => false,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
'cache' => [
|
||||
'type' => 'cache',
|
||||
'dsns' => $fallbackForRedis,
|
||||
'multiple' => true,
|
||||
'schemes' => ['redis'],
|
||||
],
|
||||
];
|
||||
|
||||
$maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151);
|
||||
$instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14);
|
||||
|
||||
$multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
|
||||
|
||||
if ($multiprocessing) {
|
||||
$workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
} else {
|
||||
$workerCount = 1;
|
||||
}
|
||||
|
||||
if ($workerCount > $instanceConnections) {
|
||||
throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500);
|
||||
}
|
||||
|
||||
$poolSize = (int)($instanceConnections / $workerCount);
|
||||
|
||||
foreach ($connections as $key => $connection) {
|
||||
$type = $connection['type'] ?? '';
|
||||
$multiple = $connection['multiple'] ?? false;
|
||||
$schemes = $connection['schemes'] ?? [];
|
||||
$config = [];
|
||||
$dsns = explode(',', $connection['dsns'] ?? '');
|
||||
foreach ($dsns as &$dsn) {
|
||||
$dsn = explode('=', $dsn);
|
||||
$name = ($multiple) ? $key . '_' . $dsn[0] : $key;
|
||||
$dsn = $dsn[1] ?? '';
|
||||
$config[] = $name;
|
||||
if (empty($dsn)) {
|
||||
//throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$dsn = new DSN($dsn);
|
||||
$dsnHost = $dsn->getHost();
|
||||
$dsnPort = $dsn->getPort();
|
||||
$dsnUser = $dsn->getUser();
|
||||
$dsnPass = $dsn->getPassword();
|
||||
$dsnScheme = $dsn->getScheme();
|
||||
$dsnDatabase = $dsn->getPath();
|
||||
|
||||
if (!in_array($dsnScheme, $schemes)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Resource
|
||||
*
|
||||
* Creation could be reused across connection types like database, cache, queue, etc.
|
||||
*
|
||||
* Resource assignment to an adapter will happen below.
|
||||
*/
|
||||
$resource = match ($dsnScheme) {
|
||||
'mysql',
|
||||
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
|
||||
\PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
\PDO::ATTR_PERSISTENT => false,
|
||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
||||
\PDO::ATTR_EMULATE_PREPARES => true,
|
||||
\PDO::ATTR_STRINGIFY_FETCHES => true
|
||||
));
|
||||
});
|
||||
},
|
||||
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
|
||||
$redis = new \Redis();
|
||||
@$redis->pconnect($dsnHost, (int)$dsnPort);
|
||||
if ($dsnPass) {
|
||||
$redis->auth($dsnPass);
|
||||
}
|
||||
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
|
||||
|
||||
return $redis;
|
||||
},
|
||||
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
|
||||
};
|
||||
|
||||
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
|
||||
// Get Adapter
|
||||
switch ($type) {
|
||||
case 'database':
|
||||
$adapter = match ($dsn->getScheme()) {
|
||||
'mariadb' => new MariaDB($resource()),
|
||||
'mysql' => new MySQL($resource()),
|
||||
default => null
|
||||
};
|
||||
|
||||
$adapter->setDatabase($dsn->getPath());
|
||||
return $adapter;
|
||||
case 'pubsub':
|
||||
return match ($dsn->getScheme()) {
|
||||
'redis' => new PubSub($resource()),
|
||||
default => null
|
||||
};
|
||||
case 'publisher':
|
||||
case 'consumer':
|
||||
return match ($dsn->getScheme()) {
|
||||
'redis' => new Queue\Broker\Redis(new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort())),
|
||||
default => null
|
||||
};
|
||||
case 'cache':
|
||||
return match ($dsn->getScheme()) {
|
||||
'redis' => new RedisCache($resource()),
|
||||
default => null
|
||||
};
|
||||
default:
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
|
||||
}
|
||||
});
|
||||
|
||||
$group->add($pool);
|
||||
}
|
||||
|
||||
Config::setParam('pools-' . $key, $config);
|
||||
}
|
||||
|
||||
return $group;
|
||||
});
|
||||
|
||||
$register->set('db', function () {
|
||||
// This is usually for our workers or CLI commands scope
|
||||
$dbHost = System::getEnv('_APP_DB_HOST', '');
|
||||
$dbPort = System::getEnv('_APP_DB_PORT', '');
|
||||
$dbUser = System::getEnv('_APP_DB_USER', '');
|
||||
$dbPass = System::getEnv('_APP_DB_PASS', '');
|
||||
$dbScheme = System::getEnv('_APP_DB_SCHEMA', '');
|
||||
|
||||
return new PDO(
|
||||
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
|
||||
$dbUser,
|
||||
$dbPass,
|
||||
SQL::getPDOAttributes()
|
||||
);
|
||||
});
|
||||
|
||||
$register->set('smtp', function () {
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
$mail->isSMTP();
|
||||
|
||||
$username = System::getEnv('_APP_SMTP_USERNAME');
|
||||
$password = System::getEnv('_APP_SMTP_PASSWORD');
|
||||
|
||||
$mail->XMailer = 'Appwrite Mailer';
|
||||
$mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp');
|
||||
$mail->Port = System::getEnv('_APP_SMTP_PORT', 25);
|
||||
$mail->SMTPAuth = !empty($username) && !empty($password);
|
||||
$mail->Username = $username;
|
||||
$mail->Password = $password;
|
||||
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
|
||||
$mail->SMTPAutoTLS = false;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
|
||||
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
|
||||
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
|
||||
$mail->setFrom($email, $from);
|
||||
$mail->addReplyTo($email, $from);
|
||||
|
||||
$mail->isHTML(true);
|
||||
|
||||
return $mail;
|
||||
});
|
||||
$register->set('geodb', function () {
|
||||
return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb');
|
||||
});
|
||||
$register->set('passwordsDictionary', function () {
|
||||
$content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords');
|
||||
$content = explode("\n", $content);
|
||||
$content = array_flip($content);
|
||||
return $content;
|
||||
});
|
||||
$register->set('promiseAdapter', function () {
|
||||
return new Swoole();
|
||||
});
|
||||
$register->set('hooks', function () {
|
||||
return new Hooks();
|
||||
});
|
||||
@@ -1,845 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\Migration;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\AWS;
|
||||
use Utopia\Storage\Device\Backblaze;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\Linode;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\Wasabi;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
|
||||
|
||||
// Runtime Execution
|
||||
App::setResource('log', fn () => new Log());
|
||||
App::setResource('logger', function ($register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('hooks', function ($register) {
|
||||
return $register->get('hooks');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('register', fn () => $register);
|
||||
App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
|
||||
|
||||
App::setResource('localeCodes', function () {
|
||||
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
|
||||
});
|
||||
|
||||
// Queues
|
||||
App::setResource('publisher', function (Group $pools) {
|
||||
return $pools->get('publisher')->pop()->getResource();
|
||||
}, ['pools']);
|
||||
App::setResource('consumer', function (Group $pools) {
|
||||
return $pools->get('consumer')->pop()->getResource();
|
||||
}, ['pools']);
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMails', function (Publisher $publisher) {
|
||||
return new Mail($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForBuilds', function (Publisher $publisher) {
|
||||
return new Build($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForEvents', function (Publisher $publisher) {
|
||||
return new Event($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForWebhooks', function (Publisher $publisher) {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForAudits', function (Publisher $publisher) {
|
||||
return new Audit($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Current Host',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'hostname' => $request->getHostname(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
|
||||
$hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
|
||||
$validator = new Hostname();
|
||||
foreach ($hostnames as $hostname) {
|
||||
$hostname = trim($hostname);
|
||||
if (!$validator->isValid($hostname)) {
|
||||
continue;
|
||||
}
|
||||
$console->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'name' => $hostname,
|
||||
'hostname' => $hostname,
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All verified client URLs for both console and current projects
|
||||
* + Filter for duplicated entries
|
||||
*/
|
||||
$clientsConsole = \array_map(
|
||||
fn ($node) => $node['hostname'],
|
||||
\array_filter(
|
||||
$console->getAttribute('platforms', []),
|
||||
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
|
||||
)
|
||||
);
|
||||
|
||||
$clients = $clientsConsole;
|
||||
$platforms = $project->getAttribute('platforms', []);
|
||||
|
||||
foreach ($platforms as $node) {
|
||||
if (
|
||||
isset($node['type']) &&
|
||||
($node['type'] === Origin::CLIENT_TYPE_WEB ||
|
||||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
|
||||
!empty($node['hostname'])
|
||||
) {
|
||||
$clients[] = $node['hostname'];
|
||||
}
|
||||
}
|
||||
|
||||
return \array_unique($clients);
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var string $mode */
|
||||
|
||||
Authorization::setDefaultStatus(true);
|
||||
|
||||
Auth::setCookieName('a_session_' . $project->getId());
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
Auth::setCookieName('a_session_' . $console->getId());
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession(
|
||||
$request->getCookie(
|
||||
Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName . '_legacy', '')
|
||||
)
|
||||
);
|
||||
|
||||
// Get session from header for SSR clients
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
$sessionHeader = $request->getHeader('x-appwrite-session', '');
|
||||
|
||||
if (!empty($sessionHeader)) {
|
||||
$session = Auth::decodeSession($sessionHeader);
|
||||
}
|
||||
}
|
||||
|
||||
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
}
|
||||
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
}
|
||||
$fallback = $request->getHeader('x-fallback-cookies', '');
|
||||
$fallback = \json_decode($fallback, true);
|
||||
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
|
||||
}
|
||||
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document([]);
|
||||
} else {
|
||||
if ($project->getId() === 'console') {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
if (
|
||||
$user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
|
||||
) { // Validate user has valid login token
|
||||
$user = new Document([]);
|
||||
}
|
||||
|
||||
// if (APP_MODE_ADMIN === $mode) {
|
||||
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
|
||||
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
|
||||
// } else {
|
||||
// $user = new Document([]);
|
||||
// }
|
||||
// }
|
||||
|
||||
$authJWT = $request->getHeader('x-appwrite-jwt', '');
|
||||
|
||||
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
|
||||
|
||||
try {
|
||||
$payload = $jwt->decode($authJWT);
|
||||
} catch (JWTException $error) {
|
||||
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
|
||||
}
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
if (!empty($jwtUserId)) {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
if (!empty($jwtSessionId)) {
|
||||
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
|
||||
$user = new Document([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->setMetadata('user', $user->getId());
|
||||
$dbForPlatform->setMetadata('user', $user->getId());
|
||||
|
||||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
|
||||
|
||||
App::setResource('project', function ($dbForPlatform, $request, $console) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var Utopia\Database\Document $console */
|
||||
|
||||
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
|
||||
|
||||
if (empty($projectId) || $projectId === 'console') {
|
||||
return $console;
|
||||
}
|
||||
|
||||
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
|
||||
|
||||
return $project;
|
||||
}, ['dbForPlatform', 'request', 'console']);
|
||||
|
||||
App::setResource('session', function (Document $user) {
|
||||
if ($user->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
|
||||
|
||||
if (!$sessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($sessions as $session) {/** @var Document $session */
|
||||
if ($sessionId === $session->getId()) {
|
||||
return $session;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}, ['user']);
|
||||
|
||||
App::setResource('console', function () {
|
||||
return new Document(Config::getParam('console'));
|
||||
}, []);
|
||||
|
||||
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($project->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'project']);
|
||||
|
||||
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
|
||||
$dbAdapter = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
|
||||
$database
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', 'console')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($project->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$configure = (function (Database $database) use ($project, $dsn) {
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId())
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
}
|
||||
});
|
||||
|
||||
if (isset($databases[$dsn->getHost()])) {
|
||||
$database = $databases[$dsn->getHost()];
|
||||
$configure($database);
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
$configure($database);
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache']);
|
||||
|
||||
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database = null;
|
||||
return function (?Document $project = null) use ($pools, $cache, $database) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get('logs')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database(
|
||||
$dbAdapter,
|
||||
$cache
|
||||
);
|
||||
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
// set tenant
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
}
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
App::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource();
|
||||
}
|
||||
|
||||
$cache = new Cache(new Sharding($adapters));
|
||||
|
||||
$cache->setTelemetry($telemetry);
|
||||
|
||||
return $cache;
|
||||
}, ['pools', 'telemetry']);
|
||||
|
||||
App::setResource('redis', function () {
|
||||
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
|
||||
$port = System::getEnv('_APP_REDIS_PORT', 6379);
|
||||
$pass = System::getEnv('_APP_REDIS_PASS', '');
|
||||
|
||||
$redis = new \Redis();
|
||||
@$redis->pconnect($host, (int)$port);
|
||||
if ($pass) {
|
||||
$redis->auth($pass);
|
||||
}
|
||||
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
|
||||
|
||||
return $redis;
|
||||
});
|
||||
|
||||
App::setResource('timelimit', function (\Redis $redis) {
|
||||
return function (string $key, int $limit, int $time) use ($redis) {
|
||||
return new TimeLimitRedis($key, $limit, $time, $redis);
|
||||
};
|
||||
}, ['redis']);
|
||||
|
||||
App::setResource('deviceForLocal', function () {
|
||||
return new Local();
|
||||
});
|
||||
|
||||
App::setResource('deviceForFiles', function ($project) {
|
||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceForFunctions', function ($project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceForBuilds', function ($project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
function getDevice(string $root, string $connection = ''): Device
|
||||
{
|
||||
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
|
||||
|
||||
if (!empty($connection)) {
|
||||
$acl = 'private';
|
||||
$device = Storage::DEVICE_LOCAL;
|
||||
$accessKey = '';
|
||||
$accessSecret = '';
|
||||
$bucket = '';
|
||||
$region = '';
|
||||
$url = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
|
||||
|
||||
try {
|
||||
$dsn = new DSN($connection);
|
||||
$device = $dsn->getScheme();
|
||||
$accessKey = $dsn->getUser() ?? '';
|
||||
$accessSecret = $dsn->getPassword() ?? '';
|
||||
$bucket = $dsn->getPath() ?? '';
|
||||
$region = $dsn->getParam('region');
|
||||
} catch (\Throwable $e) {
|
||||
Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
|
||||
}
|
||||
|
||||
switch ($device) {
|
||||
case Storage::DEVICE_S3:
|
||||
if (!empty($url)) {
|
||||
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
|
||||
} else {
|
||||
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
}
|
||||
// no break
|
||||
case STORAGE::DEVICE_DO_SPACES:
|
||||
$device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
|
||||
return $device;
|
||||
case Storage::DEVICE_BACKBLAZE:
|
||||
return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
case Storage::DEVICE_LINODE:
|
||||
return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
case Storage::DEVICE_WASABI:
|
||||
return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
}
|
||||
} else {
|
||||
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', '');
|
||||
$s3Region = System::getEnv('_APP_STORAGE_S3_REGION', '');
|
||||
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
|
||||
if (!empty($s3EndpointUrl)) {
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
|
||||
} else {
|
||||
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
}
|
||||
// no break
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
$device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
|
||||
return $device;
|
||||
case Storage::DEVICE_BACKBLAZE:
|
||||
$backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
|
||||
$backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
|
||||
$backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
|
||||
$backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
|
||||
$backblazeAcl = 'private';
|
||||
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
|
||||
case Storage::DEVICE_LINODE:
|
||||
$linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
|
||||
$linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', '');
|
||||
$linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', '');
|
||||
$linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
|
||||
$linodeAcl = 'private';
|
||||
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
|
||||
case Storage::DEVICE_WASABI:
|
||||
$wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
|
||||
$wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', '');
|
||||
$wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', '');
|
||||
$wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
|
||||
$wasabiAcl = 'private';
|
||||
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App::setResource('mode', function ($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
/**
|
||||
* Defines the mode for the request:
|
||||
* - 'default' => Requests for Client and Server Side
|
||||
* - 'admin' => Request from the Console on non-console projects
|
||||
*/
|
||||
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('geodb', function ($register) {
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
return $register->get('geodb');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('passwordsDictionary', function ($register) {
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
return $register->get('passwordsDictionary');
|
||||
}, ['register']);
|
||||
|
||||
|
||||
App::setResource('servers', function () {
|
||||
$platforms = Config::getParam('platforms');
|
||||
$server = $platforms[APP_PLATFORM_SERVER];
|
||||
|
||||
$languages = array_map(function ($language) {
|
||||
return strtolower($language['name']);
|
||||
}, $server['sdks']);
|
||||
|
||||
return $languages;
|
||||
});
|
||||
|
||||
App::setResource('promiseAdapter', function ($register) {
|
||||
return $register->get('promiseAdapter');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('schema', function ($utopia, $dbForProject) {
|
||||
|
||||
$complexity = function (int $complexity, array $args) {
|
||||
$queries = Query::parseQueries($args['queries'] ?? []);
|
||||
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
|
||||
$limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
|
||||
|
||||
return $complexity * $limit;
|
||||
};
|
||||
|
||||
$attributes = function (int $limit, int $offset) use ($dbForProject) {
|
||||
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]));
|
||||
|
||||
return \array_map(function ($attr) {
|
||||
return $attr->getArrayCopy();
|
||||
}, $attrs);
|
||||
};
|
||||
|
||||
$urls = [
|
||||
'list' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return "/v1/databases/$databaseId/collections/$collectionId/documents";
|
||||
},
|
||||
'create' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return "/v1/databases/$databaseId/collections/$collectionId/documents";
|
||||
},
|
||||
'read' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
|
||||
},
|
||||
'update' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
|
||||
},
|
||||
'delete' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
|
||||
},
|
||||
];
|
||||
|
||||
$params = [
|
||||
'list' => function (string $databaseId, string $collectionId, array $args) {
|
||||
return [ 'queries' => $args['queries']];
|
||||
},
|
||||
'create' => function (string $databaseId, string $collectionId, array $args) {
|
||||
$id = $args['id'] ?? 'unique()';
|
||||
$permissions = $args['permissions'] ?? null;
|
||||
|
||||
unset($args['id']);
|
||||
unset($args['permissions']);
|
||||
|
||||
// Order must be the same as the route params
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'documentId' => $id,
|
||||
'collectionId' => $collectionId,
|
||||
'data' => $args,
|
||||
'permissions' => $permissions,
|
||||
];
|
||||
},
|
||||
'update' => function (string $databaseId, string $collectionId, array $args) {
|
||||
$documentId = $args['id'];
|
||||
$permissions = $args['permissions'] ?? null;
|
||||
|
||||
unset($args['id']);
|
||||
unset($args['permissions']);
|
||||
|
||||
// Order must be the same as the route params
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'documentId' => $documentId,
|
||||
'data' => $args,
|
||||
'permissions' => $permissions,
|
||||
];
|
||||
},
|
||||
];
|
||||
|
||||
return Schema::build(
|
||||
$utopia,
|
||||
$complexity,
|
||||
$attributes,
|
||||
$urls,
|
||||
$params,
|
||||
);
|
||||
}, ['utopia', 'dbForProject']);
|
||||
|
||||
App::setResource('contributors', function () {
|
||||
$path = 'app/config/contributors.json';
|
||||
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
|
||||
return $list;
|
||||
});
|
||||
|
||||
App::setResource('employees', function () {
|
||||
$path = 'app/config/employees.json';
|
||||
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
|
||||
return $list;
|
||||
});
|
||||
|
||||
App::setResource('heroes', function () {
|
||||
$path = 'app/config/heroes.json';
|
||||
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
|
||||
return $list;
|
||||
});
|
||||
|
||||
App::setResource('gitHub', function (Cache $cache) {
|
||||
return new VcsGitHub($cache);
|
||||
}, ['cache']);
|
||||
|
||||
App::setResource('requestTimestamp', function ($request) {
|
||||
//TODO: Move this to the Request class itself
|
||||
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
|
||||
$requestTimestamp = null;
|
||||
if (!empty($timestampHeader)) {
|
||||
try {
|
||||
$requestTimestamp = new \DateTime($timestampHeader);
|
||||
} catch (\Throwable $e) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
|
||||
}
|
||||
}
|
||||
return $requestTimestamp;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('plan', function (array $plan = []) {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('smsRates', function () {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
|
||||
$teamInternalId = '';
|
||||
if ($project->getId() !== 'console') {
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', '');
|
||||
} else {
|
||||
$route = $utopia->match($request);
|
||||
$path = $route->getPath();
|
||||
if (str_starts_with($path, '/v1/projects/:projectId')) {
|
||||
$uri = $request->getURI();
|
||||
$pid = explode('/', $uri)[3];
|
||||
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
|
||||
$teamInternalId = $p->getAttribute('teamInternalId', '');
|
||||
} elseif ($path === '/v1/projects') {
|
||||
$teamId = $request->getParam('teamId', '');
|
||||
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
|
||||
return $team;
|
||||
}
|
||||
}
|
||||
|
||||
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
|
||||
return $dbForPlatform->findOne('teams', [
|
||||
Query::equal('$internalId', [$teamInternalId]),
|
||||
]);
|
||||
});
|
||||
|
||||
return $team;
|
||||
}, ['project', 'dbForPlatform', 'utopia', 'request']);
|
||||
|
||||
App::setResource(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
|
||||
);
|
||||
|
||||
App::setResource('previewHostname', function (Request $request) {
|
||||
if (App::isDevelopment()) {
|
||||
$host = $request->getQuery('appwrite-hostname') ?? '';
|
||||
if (!empty($host)) {
|
||||
return $host;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
|
||||
$key = $request->getHeader('x-appwrite-key');
|
||||
|
||||
if (empty($key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Key::decode($project, $key);
|
||||
}, ['request', 'project']);
|
||||
|
||||
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
@@ -0,0 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 Not Found</title>
|
||||
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
span {
|
||||
padding: var(--space-1, 2px) var(--space-3, 6px);
|
||||
border-radius: var(--border-radius-XS, 6px);
|
||||
background: var(--color-overlay-on-neutral, rgba(0, 0, 0, 0.06));
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
letter-spacing: -0.063px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--color-fgColor-neutral-primary, #2D2D31);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-XXXL, 32px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 140%;
|
||||
letter-spacing: -0.144px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: var(--border-radius-S, 8px);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 140%;
|
||||
letter-spacing: -0.063px;
|
||||
padding: var(--space-3, 6px) var(--space-5, 10px);
|
||||
cursor: pointer;
|
||||
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #D8D8DB);
|
||||
background: var(--color-bgColor-neutral-primary, #FFF);
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.brand {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.brand p {
|
||||
font-family: var(--font-family-monospace, "Aeonik Fono");
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 130%;
|
||||
letter-spacing: 0.96px;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
}
|
||||
|
||||
.brand svg {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #1D1D21;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--color-fgColor-neutral-primary, #EDEDF0);
|
||||
}
|
||||
|
||||
button {
|
||||
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #414146);
|
||||
background: var(--color-bgColor-neutral-primary, #1D1D21);
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
}
|
||||
|
||||
.brand p {
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
}
|
||||
|
||||
span {
|
||||
background: var(--color-overlay-on-neutral, rgba(255, 255, 255, 0.20));
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
}
|
||||
|
||||
.logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo-dark {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<div class="center"><span>Page not found</span></div>
|
||||
<h1>The page you’re looking for doesn’t exist.</h1>
|
||||
<div class="center"><a href="/"><button>Go to homepage</button></a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brand">
|
||||
<p>Powered by</p>
|
||||
<svg class="logo-dark" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8649 16.2461C33.6492 16.2461 34.5511 15.3184 34.9433 14.6867H35.1197C35.1981 15.3578 35.6687 15.9895 36.5903 15.9895H38.3353V14.0156H37.8843C37.5706 14.0156 37.4138 13.838 37.4138 13.5617V5.64661H35.1001V6.90986H34.9236C34.4727 6.27823 33.5315 5.39001 31.8061 5.39001C29.0611 5.39001 27.022 7.67965 27.022 10.818C27.022 13.9564 29.1003 16.2461 31.8649 16.2461ZM32.2767 13.9959C30.6493 13.9959 29.3748 12.7919 29.3748 10.8378C29.3748 8.92316 30.6101 7.62044 32.2571 7.62044C33.8256 7.62044 35.1393 8.78499 35.1393 10.8378C35.1393 12.5945 34.0217 13.9959 32.2767 13.9959Z" fill="#EDEDF0" />
|
||||
<path d="M39.7013 20H42.0149V14.6867H42.1914C42.6227 15.3184 43.5443 16.2461 45.3677 16.2461C48.1127 16.2461 50.1127 13.9169 50.1127 10.818C50.1127 7.69939 47.9755 5.39001 45.2109 5.39001C43.4462 5.39001 42.5835 6.35719 42.1718 6.89012H41.9953V5.64661H39.7013V20ZM44.8776 14.0551C43.2894 14.0551 41.9757 12.8708 41.9757 10.818C41.9757 9.06133 43.0933 7.58096 44.8383 7.58096C46.4657 7.58096 47.7402 8.86395 47.7402 10.818C47.7402 12.7326 46.5049 14.0551 44.8776 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M51.3065 20H53.6202V14.6867H53.7966C54.228 15.3184 55.1495 16.2461 56.973 16.2461C59.718 16.2461 61.5273 13.9169 61.5273 10.818C61.5273 7.69939 59.5807 5.39001 56.8161 5.39001C55.0515 5.39001 54.1888 6.35719 53.777 6.89012H53.6005V5.64661H51.3065V20ZM56.4828 14.0551C54.8946 14.0551 53.5809 12.8708 53.5809 10.818C53.5809 9.06133 54.6985 7.58096 56.4436 7.58096C58.071 7.58096 59.3454 8.86395 59.3454 10.818C59.3454 12.7326 58.1102 14.0551 56.4828 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M64.5857 16.2296H67.8601L69.7227 8.11721H69.8404L71.7031 16.2296H74.9579L77.5642 5.88678H75.2323L73.3697 14.0189H73.1932L71.3305 5.88678H68.2522L66.3699 14.0189H66.1935L64.3504 5.88678H61.8799L64.5857 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M78.7363 16.2296H81.0499V11.1174C81.0499 9.16334 81.9519 7.9593 83.6381 7.9593H84.6576V5.63019H83.893C82.5793 5.63019 81.5793 6.53815 81.1872 7.40663H81.0303V5.88678H78.7363V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M96.1391 16.2296H97.943V14.1571H96.1587C95.4529 14.1571 95.1588 13.8413 95.1588 13.111V7.93956H98.0606V5.88678H95.1588V2.98526H92.9628V5.88678H91.0413V7.93956H92.8255V13.1307C92.8255 15.3217 94.1392 16.2296 96.1391 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.17 15.1802 108.836 13.0287L106.719 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.327 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.915 7.58096 98.915 10.8378C98.915 13.9959 101.013 16.2461 104.15 16.2461ZM101.327 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.621 8.31128 106.738 9.71269H101.327Z" fill="#EDEDF0" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0125 16.2296H87.6989V7.93956H85.895V5.88678H90.0125V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M88.6834 4.45145C89.5265 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5265 1.54993 88.6834 1.54993C87.8403 1.54993 87.2129 2.18155 87.2129 2.99082C87.2129 3.81983 87.8403 4.45145 88.6834 4.45145Z" fill="#EDEDF0" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2006 7.88412V12.4486H11.9414C12.8021 11.6166 13.3389 10.4373 13.3389 9.12899C13.3389 8.69744 13.2806 8.27999 13.1713 7.88412H20.2006Z" fill="#FD366E" />
|
||||
</svg>
|
||||
<svg class="logo-light" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8648 16.2461C33.649 16.2461 34.5509 15.3184 34.9431 14.6867H35.1195C35.198 15.3578 35.6685 15.9895 36.5901 15.9895H38.3351V14.0156H37.8841C37.5704 14.0156 37.4136 13.838 37.4136 13.5617V5.64661H35.0999V6.90986H34.9235C34.4725 6.27823 33.5314 5.39001 31.8059 5.39001C29.0609 5.39001 27.0218 7.67965 27.0218 10.818C27.0218 13.9564 29.1001 16.2461 31.8648 16.2461ZM32.2765 13.9959C30.6491 13.9959 29.3746 12.7919 29.3746 10.8378C29.3746 8.92316 30.6099 7.62044 32.2569 7.62044C33.8255 7.62044 35.1391 8.78499 35.1391 10.8378C35.1391 12.5945 34.0215 13.9959 32.2765 13.9959Z" fill="#2D2D31" />
|
||||
<path d="M39.7011 20H42.0147V14.6867H42.1912C42.6226 15.3184 43.5441 16.2461 45.3676 16.2461C48.1126 16.2461 50.1125 13.9169 50.1125 10.818C50.1125 7.69939 47.9753 5.39001 45.2107 5.39001C43.4461 5.39001 42.5833 6.35719 42.1716 6.89012H41.9951V5.64661H39.7011V20ZM44.8774 14.0551C43.2892 14.0551 41.9755 12.8708 41.9755 10.818C41.9755 9.06133 43.0931 7.58096 44.8382 7.58096C46.4656 7.58096 47.74 8.86395 47.74 10.818C47.74 12.7326 46.5048 14.0551 44.8774 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M51.3063 20H53.62V14.6867H53.7964C54.2278 15.3184 55.1493 16.2461 56.9728 16.2461C59.7178 16.2461 61.5271 13.9169 61.5271 10.818C61.5271 7.69939 59.5805 5.39001 56.8159 5.39001C55.0513 5.39001 54.1886 6.35719 53.7768 6.89012H53.6004V5.64661H51.3063V20ZM56.4826 14.0551C54.8944 14.0551 53.5808 12.8708 53.5808 10.818C53.5808 9.06133 54.6984 7.58096 56.4434 7.58096C58.0708 7.58096 59.3453 8.86395 59.3453 10.818C59.3453 12.7326 58.11 14.0551 56.4826 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M64.5855 16.2296H67.8599L69.7226 8.11721H69.8402L71.7029 16.2296H74.9577L77.564 5.88678H75.2322L73.3695 14.0189H73.193L71.3303 5.88678H68.252L66.3697 14.0189H66.1933L64.3502 5.88678H61.8797L64.5855 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M78.7361 16.2296H81.0498V11.1174C81.0498 9.16334 81.9517 7.9593 83.6379 7.9593H84.6575V5.63019H83.8928C82.5791 5.63019 81.5791 6.53815 81.187 7.40663H81.0301V5.88678H78.7361V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M96.1389 16.2296H97.9428V14.1571H96.1585C95.4527 14.1571 95.1586 13.8413 95.1586 13.111V7.93956H98.0604V5.88678H95.1586V2.98526H92.9626V5.88678H91.0411V7.93956H92.8253V13.1307C92.8253 15.3217 94.139 16.2296 96.1389 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.169 15.1802 108.836 13.0287L106.718 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.326 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.9148 7.58096 98.9148 10.8378C98.9148 13.9959 101.013 16.2461 104.15 16.2461ZM101.326 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.62 8.31128 106.738 9.71269H101.326Z" fill="#2D2D31" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0123 16.2296H87.6987V7.93956H85.8948V5.88678H90.0123V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M88.6835 4.45145C89.5266 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5266 1.54993 88.6835 1.54993C87.8404 1.54993 87.213 2.18155 87.213 2.99082C87.213 3.81983 87.8404 4.45145 88.6835 4.45145Z" fill="#2D2D31" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2007 7.88412V12.4486H11.9415C12.8022 11.6166 13.339 10.4373 13.339 9.12899C13.339 8.69744 13.2807 8.27999 13.1714 7.88412H20.2007Z" fill="#FD366E" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -86,7 +86,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_OPTIONS_ABUSE
|
||||
- _APP_OPTIONS_ROUTER_PROTECTION
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_COMPUTE_FORCE_HTTPS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DOMAIN
|
||||
- _APP_DOMAIN_TARGET
|
||||
@@ -133,23 +133,23 @@ $image = $this->getParam('image', '');
|
||||
- _APP_STORAGE_WASABI_SECRET
|
||||
- _APP_STORAGE_WASABI_REGION
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
- _APP_FUNCTIONS_SIZE_LIMIT
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_COMPUTE_SIZE_LIMIT
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_FUNCTIONS_RUNTIMES
|
||||
- _APP_SITES_FRAMEWORKS
|
||||
- _APP_SITES_RUNTIMES
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_MAINTENANCE_INTERVAL
|
||||
- _APP_MAINTENANCE_DELAY
|
||||
- _APP_MAINTENANCE_START_TIME
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_SMS_PROVIDER
|
||||
@@ -166,10 +166,11 @@ $image = $this->getParam('image', '');
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: <?php echo $organization; ?>/console:5.2.58
|
||||
image: <?php echo $organization; ?>/console:5.2.27
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -342,10 +343,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
|
||||
appwrite-worker-databases:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
@@ -406,13 +404,13 @@ $image = $this->getParam('image', '');
|
||||
- _APP_VCS_GITHUB_APP_NAME
|
||||
- _APP_VCS_GITHUB_PRIVATE_KEY
|
||||
- _APP_VCS_GITHUB_APP_ID
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_FUNCTIONS_SIZE_LIMIT
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_COMPUTE_SIZE_LIMIT
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_COMPUTE_FORCE_HTTPS
|
||||
- _APP_DOMAIN
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
@@ -497,10 +495,10 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_USAGE_STATS
|
||||
@@ -654,7 +652,6 @@ $image = $this->getParam('image', '');
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
@@ -745,6 +742,34 @@ $image = $this->getParam('image', '');
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-worker-stats-usage-dump:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: worker-stats-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-stats-usage-dump
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: schedule-functions
|
||||
@@ -836,7 +861,7 @@ $image = $this->getParam('image', '');
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.6.11
|
||||
image: openruntimes/executor:0.7.0
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
@@ -848,9 +873,9 @@ $image = $this->getParam('image', '');
|
||||
# It's not possible to share mount file between 2 containers without host mount (copying is too slow)
|
||||
- /tmp:/tmp:rw
|
||||
environment:
|
||||
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
|
||||
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
|
||||
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
|
||||
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD
|
||||
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL
|
||||
- OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK
|
||||
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
|
||||
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
|
||||
- OPR_EXECUTOR_ENV=$_APP_ENV
|
||||
|
||||
+17
-16
@@ -15,9 +15,11 @@ use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\Migration;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\StatsUsageDump;
|
||||
/** remove */
|
||||
/** /remove */
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
@@ -110,8 +112,6 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
|
||||
|
||||
@@ -173,8 +173,6 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache']);
|
||||
@@ -200,7 +198,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setNamespace('logsV1')
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
|
||||
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
|
||||
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
|
||||
|
||||
// set tenant
|
||||
@@ -213,18 +211,15 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
Server::setResource('abuseRetention', function () {
|
||||
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
|
||||
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400);
|
||||
});
|
||||
|
||||
Server::setResource('auditRetention', function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE', 15778800)); // 6 months
|
||||
}
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); // 14 days
|
||||
}, ['project']);
|
||||
Server::setResource('auditRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
|
||||
});
|
||||
|
||||
Server::setResource('executionRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
|
||||
});
|
||||
|
||||
Server::setResource('cache', function (Registry $register) {
|
||||
@@ -278,6 +273,10 @@ Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) {
|
||||
return new StatsUsageDump($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
@@ -334,6 +333,10 @@ Server::setResource('pools', function (Registry $register) {
|
||||
return $register->get('pools');
|
||||
}, ['register']);
|
||||
|
||||
Server::setResource('deviceForSites', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
Server::setResource('deviceForFunctions', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
@@ -409,8 +412,6 @@ Server::setResource('logError', function (Registry $register, Document $project)
|
||||
};
|
||||
}, ['register', 'project']);
|
||||
|
||||
Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php screenshot $@
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-usage-dump $@
|
||||
+17
-14
@@ -14,7 +14,8 @@
|
||||
"test": "vendor/bin/phpunit",
|
||||
"lint": "vendor/bin/pint --test",
|
||||
"format": "vendor/bin/pint",
|
||||
"bench": "vendor/bin/phpbench run --report=benchmark"
|
||||
"bench": "vendor/bin/phpbench run --report=benchmark",
|
||||
"check": "./vendor/bin/phpstan analyse -c phpstan.neon"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -30,7 +31,7 @@
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3.0",
|
||||
"php": ">=8.0.0",
|
||||
"ext-curl": "*",
|
||||
"ext-imagick": "*",
|
||||
"ext-mbstring": "*",
|
||||
@@ -43,36 +44,37 @@
|
||||
"ext-openssl": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-sockets": "*",
|
||||
"appwrite/php-runtimes": "0.16.*",
|
||||
"appwrite/php-runtimes": "0.18.*",
|
||||
"appwrite/php-clamav": "2.0.*",
|
||||
"utopia-php/abuse": "0.52.*",
|
||||
"utopia-php/abuse": "0.51.*",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "0.55.*",
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/audit": "0.54.0",
|
||||
"utopia-php/cache": "0.12.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.69.*",
|
||||
"utopia-php/detector": "0.1.*",
|
||||
"utopia-php/database": "0.60.*",
|
||||
"utopia-php/domains": "0.5.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
"utopia-php/fetch": "0.3.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.17.*",
|
||||
"utopia-php/migration": "0.9.*",
|
||||
"utopia-php/messaging": "0.16.*",
|
||||
"utopia-php/migration": "0.6.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
"utopia-php/platform": "0.7.3",
|
||||
"utopia-php/pools": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.9.*",
|
||||
"utopia-php/queue": "0.8.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/telemetry": "0.1.*",
|
||||
"utopia-php/vcs": "0.9.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
"phpmailer/phpmailer": "6.9.1",
|
||||
@@ -87,6 +89,7 @@
|
||||
"appwrite/sdk-generator": "0.40.*",
|
||||
"phpunit/phpunit": "9.*",
|
||||
"swoole/ide-helper": "5.1.2",
|
||||
"phpstan/phpstan": "1.8.*",
|
||||
"textalk/websocket": "1.5.*",
|
||||
"laravel/pint": "1.*",
|
||||
"phpbench/phpbench": "1.*"
|
||||
|
||||
Generated
+611
-241
File diff suppressed because it is too large
Load Diff
+77
-36
@@ -76,6 +76,8 @@ services:
|
||||
- appwrite-config:/storage/config:rw
|
||||
- appwrite-certificates:/storage/certificates:rw
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-sites:/storage/sites:rw
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
- ./phpunit.xml:/usr/src/code/phpunit.xml
|
||||
- ./tests:/usr/src/code/tests
|
||||
- ./app:/usr/src/code/app
|
||||
@@ -110,7 +112,7 @@ services:
|
||||
- _APP_OPTIONS_ABUSE
|
||||
- _APP_OPTIONS_ROUTER_PROTECTION
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_COMPUTE_FORCE_HTTPS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DOMAIN
|
||||
- _APP_DOMAIN_TARGET
|
||||
@@ -157,12 +159,15 @@ services:
|
||||
- _APP_STORAGE_WASABI_SECRET
|
||||
- _APP_STORAGE_WASABI_REGION
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
- _APP_FUNCTIONS_SIZE_LIMIT
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_COMPUTE_SIZE_LIMIT
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_FUNCTIONS_RUNTIMES
|
||||
- _APP_SITES_RUNTIMES
|
||||
- _APP_SITES_FRAMEWORKS
|
||||
- _APP_DOMAIN_SITES
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_LOGGING_CONFIG
|
||||
@@ -171,7 +176,6 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_SMS_PROVIDER
|
||||
@@ -198,14 +202,11 @@ services:
|
||||
- _APP_DATABASE_SHARED_TABLES_V1
|
||||
- _APP_DATABASE_SHARED_NAMESPACE
|
||||
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
|
||||
- _APP_CUSTOM_DOMAIN_DENY_LIST
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.2.58
|
||||
image: appwrite/console:5.3.0-sites-rc.24
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -348,6 +349,7 @@ services:
|
||||
- appwrite-uploads:/storage/uploads:rw
|
||||
- appwrite-cache:/storage/cache:rw
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-sites:/storage/sites:rw
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
- appwrite-certificates:/storage/certificates:rw
|
||||
- ./app:/usr/src/code/app
|
||||
@@ -393,8 +395,6 @@ services:
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_DATABASE_SHARED_TABLES_V1
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
|
||||
appwrite-worker-databases:
|
||||
entrypoint: worker-databases
|
||||
@@ -436,7 +436,9 @@ services:
|
||||
- appwrite
|
||||
volumes:
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-sites:/storage/sites:rw
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
- appwrite-uploads:/storage/uploads:rw
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
@@ -461,13 +463,13 @@ services:
|
||||
- _APP_VCS_GITHUB_APP_NAME
|
||||
- _APP_VCS_GITHUB_PRIVATE_KEY
|
||||
- _APP_VCS_GITHUB_APP_ID
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_FUNCTIONS_SIZE_LIMIT
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_COMPUTE_SIZE_LIMIT
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_COMPUTE_FORCE_HTTPS
|
||||
- _APP_DOMAIN
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
@@ -492,8 +494,7 @@ services:
|
||||
- _APP_STORAGE_WASABI_REGION
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
- _APP_DOMAIN_SITES
|
||||
|
||||
appwrite-worker-certificates:
|
||||
entrypoint: worker-certificates
|
||||
@@ -559,10 +560,10 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_FUNCTIONS_BUILD_TIMEOUT
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_COMPUTE_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
- _APP_COMPUTE_CPUS
|
||||
- _APP_COMPUTE_MEMORY
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_USAGE_STATS
|
||||
@@ -729,10 +730,9 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_MAINTENANCE_START_TIME
|
||||
- _APP_MAINTENANCE_DELAY
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-task-stats-resources:
|
||||
@@ -765,7 +765,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_STATS_RESOURCES_INTERVAL
|
||||
|
||||
|
||||
appwrite-worker-stats-resources:
|
||||
entrypoint: worker-stats-resources
|
||||
<<: *x-logging
|
||||
@@ -796,7 +796,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
|
||||
appwrite-worker-stats-usage:
|
||||
entrypoint: worker-stats-usage
|
||||
<<: *x-logging
|
||||
@@ -828,6 +828,38 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-stats-usage-dump:
|
||||
entrypoint: worker-stats-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-stats-usage-dump
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_STATS_USAGE_DUAL_WRITING_DBS
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
entrypoint: schedule-functions
|
||||
<<: *x-logging
|
||||
@@ -919,12 +951,18 @@ services:
|
||||
environment:
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
|
||||
appwrite-browser:
|
||||
container_name: appwrite-browser
|
||||
image: appwrite/browser:0.2.2
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
openruntimes-executor:
|
||||
container_name: openruntimes-executor
|
||||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.6.11
|
||||
image: openruntimes/executor:0.7.10
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -933,19 +971,21 @@ services:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-sites:/storage/sites:rw
|
||||
# Host mount nessessary to share files between executor and runtimes.
|
||||
# It's not possible to share mount file between 2 containers without host mount (copying is too slow)
|
||||
- /tmp:/tmp:rw
|
||||
environment:
|
||||
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
|
||||
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
|
||||
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
|
||||
- OPR_EXECUTOR_IMAGE_PULL=enabled
|
||||
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD
|
||||
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL
|
||||
- OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK
|
||||
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
|
||||
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
|
||||
- OPR_EXECUTOR_ENV=$_APP_ENV
|
||||
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
|
||||
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
|
||||
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v4
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
|
||||
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
|
||||
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
|
||||
@@ -1103,5 +1143,6 @@ volumes:
|
||||
appwrite-uploads:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-sites:
|
||||
appwrite-builds:
|
||||
appwrite-config:
|
||||
appwrite-config:
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user