Compare commits
1813 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f468bfb830 | ||
|
33976c8ecc | ||
|
fd5285658f | ||
|
d5bfba3262 | ||
|
9486f0ca77 | ||
|
369cc5f555 | ||
|
eb1b9903a6 | ||
|
029943d59b | ||
|
065b39e7a4 | ||
|
6c91f1a5b3 | ||
|
3abab56650 | ||
|
890968603b | ||
|
c7b9e6f479 | ||
|
301cbcc980 | ||
|
a38a452481 | ||
|
bebe8ec887 | ||
|
e5762875a4 | ||
|
65b3804a6c | ||
|
007f7690fa | ||
|
f8c1b32541 | ||
|
72d7d3003b | ||
|
ddd30f331c | ||
|
00387be289 | ||
|
adb06baef6 | ||
|
7085b21f70 | ||
|
926451152e | ||
|
f5e2e96e95 | ||
|
fe9815a462 | ||
|
35b576dbec | ||
|
8b0a980e28 | ||
|
85334d1362 | ||
|
528ba4861d | ||
|
d35801aaa2 | ||
|
efd09e2ebb | ||
|
57063bd17d | ||
|
47bca33945 | ||
|
b972478812 | ||
|
7d182442a7 | ||
|
734d55c3cf | ||
|
8ab081ec32 | ||
|
9d4541c612 | ||
|
22de24b8ca | ||
|
5cbbd2c3b5 | ||
|
edc7f895be | ||
|
8fd2cc54c1 | ||
|
ef43f1d2ca | ||
|
21ad21cb50 | ||
|
2d27c11061 | ||
|
9ece873d62 | ||
|
18e7ef6eda | ||
|
e6c01171de | ||
|
61d44dd11f | ||
|
87fdd139b8 | ||
|
790d3bc637 | ||
|
ac7df62a04 | ||
|
070f817177 | ||
|
47d7381d60 | ||
|
2459da29c2 | ||
|
da7705b274 | ||
|
46e4a759bb | ||
|
076ceffa84 | ||
|
e50cb5f4bd | ||
|
7f9dd92a27 | ||
|
d4b7d75968 | ||
|
a47c8545c7 | ||
|
b2a57a5d6f | ||
|
87abbb07f3 | ||
|
770e337497 | ||
|
4c4ff05a46 | ||
|
8639f000ea | ||
|
10e38b441f | ||
|
87ad9c408c | ||
|
f194857ac9 | ||
|
a00ce8c92c | ||
|
2dba313100 | ||
|
774ac47373 | ||
|
25744d43b0 | ||
|
09d789e79b | ||
|
886af5ce19 | ||
|
2fb692ea45 | ||
|
84f82ee2fe | ||
|
28401962ca | ||
|
30dd7f3d90 | ||
|
185cb2dc3a | ||
|
144d73730d | ||
|
9a42b75f00 | ||
|
6812f811b1 | ||
|
7c154c6afd | ||
|
42aecb4c13 | ||
|
028ad4124c | ||
|
0a4739c732 | ||
|
a46ab86adf | ||
|
186024a058 | ||
|
e645ae9561 | ||
|
7fe137d2f7 | ||
|
49b182cd51 | ||
|
da8abef56d | ||
|
1f98eae1cf | ||
|
928102284a | ||
|
10f7916f37 | ||
|
b70de2cf69 | ||
|
ebb3631216 | ||
|
ad459ab837 | ||
|
941c9d0d42 | ||
|
a524182a17 | ||
|
ba444797d2 | ||
|
3b6ab82db7 | ||
|
c62b88e3f2 | ||
|
58410a54b0 | ||
|
730f196b96 | ||
|
53275b949e | ||
|
3f62b119e4 | ||
|
d19b625394 | ||
|
3dac956587 | ||
|
b95d944625 | ||
|
194ad2c90b | ||
|
3a2f007eb0 | ||
|
793fb7499d | ||
|
746da5ccda | ||
|
ac15c26c29 | ||
|
5d5d1b528e | ||
|
65f04e6046 | ||
|
1e75e74f6b | ||
|
6c835085a3 | ||
|
e09301f414 | ||
|
ac07bfb018 | ||
|
df2f4052b8 | ||
|
ef69c655cc | ||
|
e02c45941d | ||
|
5da51771cd | ||
|
b6985482f7 | ||
|
ec745b32de | ||
|
38f5f9cf5b | ||
|
f0fff3eb10 | ||
|
3d7f68c273 | ||
|
4b78546135 | ||
|
3f9ec3de82 | ||
|
a72a939334 | ||
|
e046a987f6 | ||
|
20a87b2c7f | ||
|
1cb8600a45 | ||
|
330e320b40 | ||
|
8bae14591b | ||
|
15fc2b76f9 | ||
|
42ab93c8b2 | ||
|
c6d43115c2 | ||
|
7efa3d02c7 | ||
|
65e7c6d9b5 | ||
|
b9c727a945 | ||
|
30342c865a | ||
|
3064917881 | ||
|
6e075b9cc4 | ||
|
8096df1055 | ||
|
29e2d9e1b7 | ||
|
d7f8e2ea41 | ||
|
a7f75485d1 | ||
|
c6e765f81e | ||
|
17af4d27da | ||
|
f7a6f9489d | ||
|
4b794e134d | ||
|
8864009e8d | ||
|
6465972caf | ||
|
9f6662fcc9 | ||
|
86f138ed16 | ||
|
8b61683f9e | ||
|
1889526e23 | ||
|
c39183cc62 | ||
|
d0d65b5a28 | ||
|
ef11347281 | ||
|
0aa9c05744 | ||
|
05f90e3695 | ||
|
56efc28189 | ||
|
c946ba3fae | ||
|
50c90a909e | ||
|
f92f1ee80a | ||
|
554f659f2a | ||
|
3da1cc7d5e | ||
|
d149c2dc78 | ||
|
68833a50d4 | ||
|
bac8227519 | ||
|
f4d549d300 | ||
|
f8b54d229f | ||
|
9b32898e3c | ||
|
d16c3342ce | ||
|
94f9cb0855 | ||
|
8971bb8cf2 | ||
|
5aae71cf60 | ||
|
9513bf3279 | ||
|
0ddbccf7e6 | ||
|
64a5561b5a | ||
|
6a3f9b7e53 | ||
|
1d8b693d31 | ||
|
40dd19be37 | ||
|
4959ead07c | ||
|
f8160b68b3 | ||
|
09a87b2cdb | ||
|
661a89ea74 | ||
|
7fa4144d48 | ||
|
011437dcb5 | ||
|
7b7c26063e | ||
|
bda0f7ac73 | ||
|
bd9e47e9be | ||
|
25dd523887 | ||
|
b0b484ba18 | ||
|
47ea318479 | ||
|
2288d50a7b | ||
|
2492c12281 | ||
|
675f4705c7 | ||
|
8fefcb86ea | ||
|
6f3d934bc1 | ||
|
7607f4778b | ||
|
2058af4a26 | ||
|
10680f93e7 | ||
|
0c5db3163a | ||
|
8c96fd4967 | ||
|
ccc2f608c5 | ||
|
b65a9b7243 | ||
|
14a300b428 | ||
|
78b89e7a5d | ||
|
e1b79c69e1 | ||
|
e926811724 | ||
|
447d733913 | ||
|
dc8eb5305f | ||
|
fdda332e4d | ||
|
7c12c7b124 | ||
|
988befb059 | ||
|
b0e3fffa1f | ||
|
2caeeb951f | ||
|
d97e383851 | ||
|
075e162319 | ||
|
c593d6df9c | ||
|
a060beee72 | ||
|
5c609c7419 | ||
|
0e661dd2e9 | ||
|
08721170da | ||
|
6a147f8377 | ||
|
50f226348f | ||
|
45c0f5433c | ||
|
55880c7098 | ||
|
0b62585748 | ||
|
fe56d26f7b | ||
|
50d8cf8aed | ||
|
11c515e7e2 | ||
|
562250246c | ||
|
dddf022aae | ||
|
e3764bdb52 | ||
|
18eb565755 | ||
|
f5c052b5e6 | ||
|
1bfe735d57 | ||
|
11658d8653 | ||
|
5b2b493a90 | ||
|
cc26fd71ac | ||
|
3b372c0041 | ||
|
702a926299 | ||
|
ad94e1d50a | ||
|
4bfd786550 | ||
|
9463bba5fb | ||
|
ceed1ebe5b | ||
|
648d6d7247 | ||
|
5e1767173f | ||
|
2bbec9f4da | ||
|
81697997a3 | ||
|
efdfea3f3c | ||
|
b31449cd77 | ||
|
f3a12ddfd0 | ||
|
b534249a8d | ||
|
cabdbb7f9c | ||
|
8adf485c0f | ||
|
c597249124 | ||
|
da13fa5021 | ||
|
a791bac153 | ||
|
b9c3788485 | ||
|
55c856c522 | ||
|
b378b4c5c7 | ||
|
5129f6f2aa | ||
|
104d089df1 | ||
|
6c9be61f57 | ||
|
f37fafe30b | ||
|
5c42e8b51b | ||
|
b4ba4b1b5d | ||
|
df06f53f9b | ||
|
3305484028 | ||
|
22e46ebad8 | ||
|
cd049454be | ||
|
cfe3cd58d6 | ||
|
2a59c6a5e7 | ||
|
93537c5560 | ||
|
800325f452 | ||
|
95bd0d4528 | ||
|
2bba6e582d | ||
|
29da56cf75 | ||
|
793eea2982 | ||
|
b4fc810bc3 | ||
|
ac092d03e7 | ||
|
869a3af3c0 | ||
|
b37ae64578 | ||
|
a083604d1d | ||
|
a2cabf3f4a | ||
|
23752639b2 | ||
|
2f34b747b3 | ||
|
9d58daac6c | ||
|
7786e1357b | ||
|
f9b23a5d62 | ||
|
248df68c36 | ||
|
43b8df3228 | ||
|
5b5ae01824 | ||
|
69ba8f063c | ||
|
885711afb9 | ||
|
66dbb59aa1 | ||
|
63484ee17a | ||
|
6cb3514d64 | ||
|
d1c2c917d9 | ||
|
b48e67fe1d | ||
|
3cf0cc7435 | ||
|
42573b76f1 | ||
|
56f882aed6 | ||
|
802cf6a4c5 | ||
|
ad41806e53 | ||
|
1b282299df | ||
|
4bdab203ac | ||
|
2374a00c10 | ||
|
28de046b8b | ||
|
025a06d322 | ||
|
513cb20b75 | ||
|
f06fa09962 | ||
|
d98de8ada7 | ||
|
83746b6364 | ||
|
6a0d4d36ad | ||
|
8ac5e651e1 | ||
|
b34d6238cb | ||
|
5fbf12bbb8 | ||
|
6226aa83d7 | ||
|
25f6f41052 | ||
|
1d1e0171ec | ||
|
28163528e5 | ||
|
8fe1f8d4ce | ||
|
62cd097414 | ||
|
9e75aa30cd | ||
|
0d1d9b9a33 | ||
|
91c929a42c | ||
|
59c68c1a74 | ||
|
19b07ba260 | ||
|
58ffe3f7c3 | ||
|
025fbb8285 | ||
|
f13afa1ee9 | ||
|
0fc0980de1 | ||
|
9dd5639f90 | ||
|
c6eab9e0aa | ||
|
abc5548cca | ||
|
7a0f781aa9 | ||
|
298ee84488 | ||
|
85bb32c410 | ||
|
5059989cc7 | ||
|
edb1de7800 | ||
|
d010816ba8 | ||
|
78fa926ed5 | ||
|
bf1bde5d6a | ||
|
59f7f4c923 | ||
|
1ee675d68b | ||
|
4601a58ac2 | ||
|
106fa28a00 | ||
|
c98681c358 | ||
|
d78474264d | ||
|
be0b372a22 | ||
|
4f24dc31dc | ||
|
625b5a567b | ||
|
af912fb308 | ||
|
4df9cabb22 | ||
|
b0f4fe456b | ||
|
aaac14b8ad | ||
|
ec2c516ab8 | ||
|
464daffdf9 | ||
|
be13e95d06 | ||
|
8e111b753a | ||
|
018a9e4e7f | ||
|
b7091c6c0f | ||
|
39e361a56d | ||
|
2aeeffc3ec | ||
|
110b3f6335 | ||
|
38e39c9366 | ||
|
cbc2e6bd40 | ||
|
44680c46ed | ||
|
7b5ea7270d | ||
|
84e2446d26 | ||
|
e8b7f3f3e2 | ||
|
3d7979c2ca | ||
|
d2e0818d5a | ||
|
5b8603879f | ||
|
8c279b1648 | ||
|
aff6a15061 | ||
|
0dcc1950d1 | ||
|
f2404de871 | ||
|
80176a3814 | ||
|
d974c94478 | ||
|
58cc673882 | ||
|
1ab8b4b8ea | ||
|
c52bcc0331 | ||
|
23b22d0dff | ||
|
dad8a1baf2 | ||
|
4da03a298a | ||
|
cc56f2230a | ||
|
9ae53ad59e | ||
|
13ac8ca66a | ||
|
60df87f6f0 | ||
|
e7e577dd6e | ||
|
d425d30804 | ||
|
07b799468d | ||
|
1656663598 | ||
|
18831acc10 | ||
|
06c83527f5 | ||
|
0a3cc246ac | ||
|
1d773b3a46 | ||
|
79a1f667c5 | ||
|
cc94b1d95a | ||
|
2ccef52a4f | ||
|
bb71538bb5 | ||
|
e23b26178a | ||
|
0fb0037ca7 | ||
|
3c35b34b61 | ||
|
d199806969 | ||
|
c9739ca86b | ||
|
9ad334f65c | ||
|
ba6a592b39 | ||
|
69bf116345 | ||
|
b62ec00d87 | ||
|
7e638043b9 | ||
|
6277a6db2a | ||
|
f6910fba02 | ||
|
3c3cf7e208 | ||
|
2bcdfcdee3 | ||
|
69c34f3438 | ||
|
8241f162df | ||
|
0d13e30ad2 | ||
|
7e2678f7f6 | ||
|
a23ac107e4 | ||
|
8f64b17d98 | ||
|
daacf15d72 | ||
|
04587116bd | ||
|
2354b10eb5 | ||
|
7a68608237 | ||
|
dc62195a06 | ||
|
68cccb00ef | ||
|
955d5d36e8 | ||
|
af161e849d | ||
|
d0d23b8f0a | ||
|
0eacf2b419 | ||
|
0f0576ea06 | ||
|
2a176514be | ||
|
38e9662d78 | ||
|
0180037dfb | ||
|
5a6645c922 | ||
|
e9b322d0a6 | ||
|
e709107463 | ||
|
9b75dee316 | ||
|
47c59591e0 | ||
|
c8c25ea7eb | ||
|
37d428dfaf | ||
|
e55dce3176 | ||
|
401559c376 | ||
|
7ac5151b74 | ||
|
d1a6b144ef | ||
|
89dc0c98ee | ||
|
ffd444d221 | ||
|
bfce6a5485 | ||
|
20fefdb714 | ||
|
1ca4e51eb3 | ||
|
451e585b97 | ||
|
30e1da7668 | ||
|
6b9e03e002 | ||
|
ef2bcf51d7 | ||
|
cfeb3beb4e | ||
|
cd509d2146 | ||
|
9804ec3a6d | ||
|
c24d62220d | ||
|
c930b61770 | ||
|
617f40fc2b | ||
|
f89c595ea0 | ||
|
404c7702ec | ||
|
17b928502a | ||
|
cb36ab9a10 | ||
|
78f036b7d2 | ||
|
8461cd4bb5 | ||
|
baff4a7ce0 | ||
|
32a4b524fb | ||
|
4b198b172d | ||
|
1f6ed4f86a | ||
|
ca2cc556f1 | ||
|
b2051a6256 | ||
|
1e65cdf821 | ||
|
cdfe51e325 | ||
|
7da74e3157 | ||
|
f2d9a3c239 | ||
|
0bfa0f2374 | ||
|
f3af1a96a8 | ||
|
9dc413b025 | ||
|
8ff0d64d05 | ||
|
5b6db8a62a | ||
|
4045b50bd6 | ||
|
bbbe3ed6ba | ||
|
6d45f768a6 | ||
|
b0968623fa | ||
|
23e1f143de | ||
|
30b377cc15 | ||
|
da8fe8079e | ||
|
ac82c9380f | ||
|
2092d5c0ad | ||
|
8fea9cc311 | ||
|
dfd9cd463d | ||
|
ddd0bb69e1 | ||
|
cdb101340a | ||
|
fbee9b5ac8 | ||
|
20bb90ced6 | ||
|
905bd24788 | ||
|
523d8f20e3 | ||
|
995f8b389a | ||
|
349d0196b2 | ||
|
097fd44978 | ||
|
11a7f725da | ||
|
45b5e60909 | ||
|
50a2854f92 | ||
|
23955d956e | ||
|
245fa1446a | ||
|
f0f8b242c4 | ||
|
7f59206944 | ||
|
459394a020 | ||
|
c3b40a6950 | ||
|
cd1b90d223 | ||
|
926bfce465 | ||
|
80bda9719d | ||
|
2a1089839d | ||
|
d017f240f9 | ||
|
86cce466b6 | ||
|
1a9124f50b | ||
|
fc09b3722a | ||
|
a04dca36a3 | ||
|
908a9671d0 | ||
|
50689f0d41 | ||
|
7e9e7c642d | ||
|
434ec913dd | ||
|
63b05096c7 | ||
|
eb6573e926 | ||
|
a58ec29631 | ||
|
bb21c6414d | ||
|
b347d55432 | ||
|
7eec279c7f | ||
|
31e5d7efd7 | ||
|
5e9d51c24b | ||
|
9c08a368e7 | ||
|
a9ef82febb | ||
|
ea23ef8cb6 | ||
|
0f6622f7d1 | ||
|
b469a8d688 | ||
|
97b2ba4240 | ||
|
3b8023f9f9 | ||
|
0df91c7b1e | ||
|
09147186b7 | ||
|
53f0452b70 | ||
|
ca85658975 | ||
|
33dd9bf36d | ||
|
c3b3594305 | ||
|
1cc775200e | ||
|
4f42238c29 | ||
|
f972815f1b | ||
|
79a468016a | ||
|
ad8814232f | ||
|
0338da1699 | ||
|
5b47774ab8 | ||
|
6151308c47 | ||
|
0979d4275a | ||
|
7e27a3c225 | ||
|
01ee460ceb | ||
|
a99179d31f | ||
|
268d90e810 | ||
|
35ae960122 | ||
|
6310dd233a | ||
|
7086aa598b | ||
|
10f51c9886 | ||
|
91e5d9f8af | ||
|
3df8c40508 | ||
|
17dc07f667 | ||
|
a043f8d277 | ||
|
fc132915ec | ||
|
18e1de6a16 | ||
|
85470ec872 | ||
|
6671297e7a | ||
|
6ba7d9d0d9 | ||
|
9b9e96eae5 | ||
|
0c3ce41031 | ||
|
2304d52599 | ||
|
158cd7ee74 | ||
|
59b42188a7 | ||
|
683707839f | ||
|
c75493755f | ||
|
b7b331ad0d | ||
|
12fa2500c4 | ||
|
d8b4f39007 | ||
|
aa177bdca7 | ||
|
fab65fb7e5 | ||
|
bd0791d800 | ||
|
5fb013878f | ||
|
dc73241bd9 | ||
|
1637d24af4 | ||
|
e3fb528d12 | ||
|
e0354aba7c | ||
|
bb0edbd988 | ||
|
64d7a63f18 | ||
|
c0fc4e9935 | ||
|
1bbe81030c | ||
|
5bf5003384 | ||
|
73923ea6c4 | ||
|
c7405fda11 | ||
|
e18390cfe6 | ||
|
22caa32ba2 | ||
|
6b2f4f8c09 | ||
|
0deb9fa6b9 | ||
|
00512ecf87 | ||
|
89c86ee521 | ||
|
6e6c0e9613 | ||
|
8d6e4e0485 | ||
|
73c0c36e7b | ||
|
69b45350fe | ||
|
bfa12239e8 | ||
|
15ce60f610 | ||
|
fb1ae0152d | ||
|
19b4c666f7 | ||
|
1e938b966e | ||
|
0ce5339a7b | ||
|
691107263c | ||
|
648a22637c | ||
|
063d4d4ccc | ||
|
c61c4565ab | ||
|
ad40bf5e0c | ||
|
a29f196f95 | ||
|
3623aea6c9 | ||
|
1a7a74ff76 | ||
|
9130b3cda9 | ||
|
a16e06bbf5 | ||
|
a7d726c383 | ||
|
461542784b | ||
|
7706ed038f | ||
|
0345cd5a0f | ||
|
90908fc24b | ||
|
13b60e6a14 | ||
|
8bb74e50be | ||
|
90b64c0069 | ||
|
e599d7caf2 | ||
|
d95642f6d9 | ||
|
03f4c214b4 | ||
|
c0355878ba | ||
|
04a2cf8bcc | ||
|
b0b34a5e38 | ||
|
b87a1229c7 | ||
|
ab36e0ef72 | ||
|
dfbadd6837 | ||
|
9bd23dc4e5 | ||
|
63c7b91572 | ||
|
182bdbc5f4 | ||
|
422f92f3f8 | ||
|
07054ee6f7 | ||
|
a0b4754231 | ||
|
62cb3b199f | ||
|
ebf2fef029 | ||
|
d87649db07 | ||
|
c7ac039697 | ||
|
023fe5181b | ||
|
8182b61518 | ||
|
7ea91dcbb3 | ||
|
6042403621 | ||
|
3f8f5642a1 | ||
|
cdbdf7f98b | ||
|
2587fcdd27 | ||
|
d8864b9e9d | ||
|
6d99a0b652 | ||
|
4d81809f36 | ||
|
7745a22ec7 | ||
|
f8cf85db3b | ||
|
39d70f375f | ||
|
1a564df586 | ||
|
4c9d5a500d | ||
|
12e590edd7 | ||
|
36e47a31e3 | ||
|
4eeda67727 | ||
|
06252ec71e | ||
|
92b09d90c8 | ||
|
a744042cf1 | ||
|
810f92e697 | ||
|
5ea643b279 | ||
|
40ef46dbef | ||
|
46061dc041 | ||
|
292c987522 | ||
|
22e067bf5c | ||
|
32d4372381 | ||
|
e583f110c3 | ||
|
41b2cfe5b8 | ||
|
ea969000a5 | ||
|
7403e5d306 | ||
|
05f8c375a2 | ||
|
9ada532809 | ||
|
6eb2bc4348 | ||
|
779eb07d75 | ||
|
0bdda362c9 | ||
|
07672e0609 | ||
|
6aa5ea1b5d | ||
|
8378b72eba | ||
|
5910eb9b61 | ||
|
50db106252 | ||
|
4e1400cecb | ||
|
bcbb6aa46f | ||
|
1bbe12254d | ||
|
93c66f0c03 | ||
|
587da93152 | ||
|
4b94e9c65e | ||
|
1951ff41b3 | ||
|
138512d204 | ||
|
7871d29aff | ||
|
0b1f88cfd5 | ||
|
97f02f2c08 | ||
|
9422b3e0d8 | ||
|
2beeea1e7d | ||
|
57b503d4ef | ||
|
dafd7afc5e | ||
|
1e02dc8715 | ||
|
919eef3098 | ||
|
7293b9fc61 | ||
|
b48a166c82 | ||
|
dfb6907e08 | ||
|
6bed372ad2 | ||
|
5e87c79d25 | ||
|
2b97451168 | ||
|
4ea376121a | ||
|
f60c1d3989 | ||
|
dbf65e1b76 | ||
|
53c2164e9c | ||
|
77cd6b5096 | ||
|
55fd55714a | ||
|
65d6b253fb | ||
|
17c1a62ec8 | ||
|
4511a10e66 | ||
|
918cfd3be6 | ||
|
ff84c18e3d | ||
|
d47091eb97 | ||
|
1f74c1dbcb | ||
|
3705cd8322 | ||
|
7fe2993b87 | ||
|
03b69ebc45 | ||
|
ed4bae182b | ||
|
8756fd1e82 | ||
|
d8b3f5fb9a | ||
|
d75b63e4fb | ||
|
2226d987f9 | ||
|
3793e598d0 | ||
|
42a1231245 | ||
|
416f644505 | ||
|
97e43ec5f0 | ||
|
f31e58af9e | ||
|
f1ed855f96 | ||
|
d9b2f84c92 | ||
|
f77b11cd10 | ||
|
0f2fbf7d05 | ||
|
7467361d70 | ||
|
f9afd06221 | ||
|
4d706f9976 | ||
|
352bae8c3e | ||
|
b241cb2704 | ||
|
be7eeb7856 | ||
|
50491e0d92 | ||
|
1337ca837b | ||
|
b4fb766b23 | ||
|
9a794067f7 | ||
|
bd5f57cbc3 | ||
|
0a7e8320b2 | ||
|
6588f6a0a9 | ||
|
ac788ad47e | ||
|
edf882320a | ||
|
16fee0335f | ||
|
dc010cc77a | ||
|
ce35d81db7 | ||
|
35eff337d5 | ||
|
6832110af4 | ||
|
d2ee48977c | ||
|
40097f438b | ||
|
e0b1e17bd0 | ||
|
01dfd6dbc8 | ||
|
b1938d7853 | ||
|
993e68a7dd | ||
|
6208ea5a53 | ||
|
42cd363542 | ||
|
0f0cc3f2eb | ||
|
d185f3ddaf | ||
|
db012b57c2 | ||
|
ea4e243303 | ||
|
95595ccd21 | ||
|
e06fbc4fcf | ||
|
5c7bed6bbc | ||
|
e571de29bf | ||
|
50ed1e83ac | ||
|
61a9018607 | ||
|
bd10a7e480 | ||
|
8c35d163a5 | ||
|
39efc6d533 | ||
|
b611dbac79 | ||
|
2f63d52b92 | ||
|
c7d1a2e400 | ||
|
660db468c0 | ||
|
8da4bf0f90 | ||
|
80b23a6a85 | ||
|
c947e2e4c5 | ||
|
661f7e6d9d | ||
|
2ef9d65052 | ||
|
66359ec522 | ||
|
c73ce7b695 | ||
|
ef7d64c801 | ||
|
6793bec4c6 | ||
|
d1aef17f9a | ||
|
2c1f7b2ece | ||
|
d181aad033 | ||
|
00ff8dc00a | ||
|
251bbf9728 | ||
|
a78b27c7cc | ||
|
c61ddd8249 | ||
|
a24605961a | ||
|
8c601b54cc | ||
|
a7e71bbd08 | ||
|
b1d4471e36 | ||
|
7495a3470e | ||
|
e9c5c16ba0 | ||
|
581a5c9d29 | ||
|
d5fa4fbcd2 | ||
|
dfa6bccb64 | ||
|
58852695c8 | ||
|
ab773e4d5f | ||
|
bd36791832 | ||
|
28b366d065 | ||
|
ad5d3134e4 | ||
|
a5293fdf61 | ||
|
a3d84e705a | ||
|
28bd4b9800 | ||
|
658cbc9425 | ||
|
cb5b5cb5f7 | ||
|
71a7cea73f | ||
|
d0cdd5cf94 | ||
|
cae933510c | ||
|
f62539ce5c | ||
|
c5dcd7d836 | ||
|
965345316f | ||
|
6c40e567aa | ||
|
dc786c0cf4 | ||
|
86efccce2a | ||
|
705f1d7bf1 | ||
|
16468bdf1b | ||
|
f62ee1ddb0 | ||
|
295e3ef02b | ||
|
54f34d3f2a | ||
|
da61352fab | ||
|
1c3ace23cb | ||
|
fc01ae31c6 | ||
|
a872392cd9 | ||
|
63553c6b5c | ||
|
36b6631c12 | ||
|
3afdd6a17b | ||
|
fa5b28df8a | ||
|
eb593a5a0c | ||
|
f58dcbc981 | ||
|
9d4710ed00 | ||
|
bfc41711dd | ||
|
7681ad8044 | ||
|
306267dbd2 | ||
|
60b871d56c | ||
|
495303d9b8 | ||
|
53b1d88873 | ||
|
1258efa882 | ||
|
06817b3c1f | ||
|
3ccca6cece | ||
|
9613a53cb3 | ||
|
7db7d68136 | ||
|
3bf6da1ffc | ||
|
0758b00bfd | ||
|
660cb058e1 | ||
|
05fb6f096d | ||
|
75c4ab9d12 | ||
|
4ca2f73b12 | ||
|
b305a23933 | ||
|
3c5006ec7f | ||
|
597948fb13 | ||
|
ca9192d9ba | ||
|
648d645c2f | ||
|
3fa3161472 | ||
|
3f6893c641 | ||
|
9b8bb2a5df | ||
|
b8f0cfd6e3 | ||
|
a4a36d994b | ||
|
d10447c3a8 | ||
|
bfe26ef67b | ||
|
1a27f9f46f | ||
|
b438224751 | ||
|
84214b864c | ||
|
87e3f0a41d | ||
|
23106844a1 | ||
|
ee2e0f694a | ||
|
4e35ce8269 | ||
|
6f63cbb53c | ||
|
084cf0babf | ||
|
a9c440637c | ||
|
1663368724 | ||
|
ca2cbe8f0f | ||
|
b9c35785e2 | ||
|
ba917e15f6 | ||
|
ff87d1bc3e | ||
|
1957209efd | ||
|
74dae9458d | ||
|
0ba49eca8b | ||
|
7162a28c34 | ||
|
156b916caf | ||
|
aedfea3554 | ||
|
fad7b9f5f2 | ||
|
e5dd385431 | ||
|
11715454d0 | ||
|
897199910f | ||
|
204d72fbe4 | ||
|
ef12a2b74c | ||
|
07a7d5959c | ||
|
aab5581c43 | ||
|
bb58fc003b | ||
|
727917e91e | ||
|
609bf93029 | ||
|
3c722fe687 | ||
|
7e0aed398f | ||
|
1c379b7ef4 | ||
|
1a37d7e252 | ||
|
a1049e9380 | ||
|
7c43ed04fe | ||
|
9f5ae7c2e8 | ||
|
fa04945365 | ||
|
1e87ed44d5 | ||
|
fed0b5ed04 | ||
|
78ed4ab75f | ||
|
85ab30abf7 | ||
|
778562c223 | ||
|
9e45b051cf | ||
|
14d86eb0d0 | ||
|
50529cbceb | ||
|
8e88a18316 | ||
|
12f5f13fab | ||
|
519119f657 | ||
|
d9b62e34da | ||
|
45c9f16f71 | ||
|
49bbef1202 | ||
|
8f800ad691 | ||
|
219a4423d8 | ||
|
e6e93ecd8a | ||
|
80a944c882 | ||
|
0c52654b52 | ||
|
904a2479dd | ||
|
e057c0e525 | ||
|
07d90b0414 | ||
|
498327b2e3 | ||
|
0893b16695 | ||
|
c9cbb8de70 | ||
|
cd0eaa349c | ||
|
1364e9e4ae | ||
|
1ed1014546 | ||
|
b83ce18b30 | ||
|
d4de2239b0 | ||
|
4a9becfca2 | ||
|
b65eb00c53 | ||
|
b5726def55 | ||
|
98146281e1 | ||
|
b08ab329f4 | ||
|
f1867a7388 | ||
|
1c1042556d | ||
|
7a81082704 | ||
|
07176fed37 | ||
|
d8d4217959 | ||
|
6ff3b3e4db | ||
|
2e59751823 | ||
|
1c293086a1 | ||
|
e85cffb236 | ||
|
36eac8ba90 | ||
|
2c51bc0ca5 | ||
|
e7a1716701 | ||
|
4fd71accd4 | ||
|
3f51c6efaa | ||
|
b04f73ce66 | ||
|
24611d8deb | ||
|
f890d2a766 | ||
|
33513753b9 | ||
|
123a343d11 | ||
|
f464f98fd3 | ||
|
6a895e1ab3 | ||
|
993ce0e5a7 | ||
|
929f58f180 | ||
|
b7d633c1bb | ||
|
3886bfb5eb | ||
|
fb3dc00dda | ||
|
e573bb0990 | ||
|
a6c129ddbd | ||
|
47cee7cc8e | ||
|
947eedcab2 | ||
|
d1f34151ae | ||
|
f1f846045f | ||
|
41452e8302 | ||
|
9ed5eebd7c | ||
|
5021c4e9ca | ||
|
4f9136d2d5 | ||
|
3523aa440b | ||
|
f5ed5f3860 | ||
|
2f3ac14a43 | ||
|
ca42f9b0eb | ||
|
31e7b73084 | ||
|
a1d0915585 | ||
|
2a90da1837 | ||
|
40e5d2303b | ||
|
18965cb0e6 | ||
|
f691afaae9 | ||
|
605a92b460 | ||
|
3b2c7a33a9 | ||
|
85a395fab6 | ||
|
cbf97c03bb | ||
|
9a1a55ce52 | ||
|
59657e24b9 | ||
|
fe398a098e | ||
|
28384c1771 | ||
|
ff7941e652 | ||
|
1c15329cce | ||
|
b2a4ffd3a9 | ||
|
fa310695fa | ||
|
580835ab69 | ||
|
54b273bf99 | ||
|
4e71b104e6 | ||
|
65c10c0bc8 | ||
|
ecdc5957a3 | ||
|
6cc432bbc4 | ||
|
dafae9818d | ||
|
9fe1619db9 | ||
|
da70aca28e | ||
|
6f531d140b | ||
|
f66a786029 | ||
|
d97903a358 | ||
|
93897134ca | ||
|
a6b59cd1a3 | ||
|
f64af6473f | ||
|
ac49c7932d | ||
|
61dcb686a8 | ||
|
9381a7d9d5 | ||
|
a5c6c748e0 | ||
|
36b5703796 | ||
|
ff6b8a6443 | ||
|
6b76a6212d | ||
|
33ee347c99 | ||
|
0306e3e9be | ||
|
357f9298bd | ||
|
f7c46fc113 | ||
|
74c39fada0 | ||
|
f02411da40 | ||
|
a568e3ca8e | ||
|
3b440bd5af | ||
|
39f27b6cf3 | ||
|
721234230c | ||
|
566ace2d64 | ||
|
092f1df9d0 | ||
|
844616e950 | ||
|
40871caa4b | ||
|
cdf8b92fea | ||
|
0074cad44f | ||
|
4a0a19fe54 | ||
|
338bff8b93 | ||
|
b88fcd53f7 | ||
|
ca7e6a6d2e | ||
|
f0cd957c7a | ||
|
64fc8d2b07 | ||
|
fd385e256d | ||
|
03119c857b | ||
|
2ef1ce1182 | ||
|
eb2425b53b | ||
|
79d3a8553f | ||
|
7709556673 | ||
|
f0ae6b4cc5 | ||
|
9e3a6d6784 | ||
|
8bf3e750ab | ||
|
18241ccbe1 | ||
|
0dccb398bd | ||
|
386365090c | ||
|
d9500c8a3b | ||
|
f7c1668bf6 | ||
|
051b649628 | ||
|
f5f165a5eb | ||
|
f89ff65ec7 | ||
|
48b940d5c6 | ||
|
6ae70a92c9 | ||
|
fa5c867e0e | ||
|
641abe2db7 | ||
|
4f7f6b3922 | ||
|
8b14726f5b | ||
|
9090b63831 | ||
|
ab27dccba5 | ||
|
56eb5c3f34 | ||
|
56333cca88 | ||
|
1aaec701bb | ||
|
cd252b794e | ||
|
b6003afcdb | ||
|
f5ee2d469b | ||
|
37b043d447 | ||
|
36579bac88 | ||
|
4476a45444 | ||
|
58a4633707 | ||
|
494969d394 | ||
|
c1a41181c5 | ||
|
6e309f5e45 | ||
|
e5f18ace2a | ||
|
11697d6894 | ||
|
675b8fea53 | ||
|
4c16ddf588 | ||
|
5ba4c36f95 | ||
|
ccd53e983c | ||
|
ff44b2e92d | ||
|
188aa3ea50 | ||
|
bd077ad7d9 | ||
|
a29d409e20 | ||
|
5acd5315f2 | ||
|
b79ab15859 | ||
|
77406d3a09 | ||
|
510c9049c7 | ||
|
ed902581d3 | ||
|
64db9ed5f6 | ||
|
1085ef3836 | ||
|
86a9de6753 | ||
|
83c982b458 | ||
|
9aba44ea79 | ||
|
6dcf96271e | ||
|
4ca60c665e | ||
|
b170627ceb | ||
|
a1b065700a | ||
|
8de048fcdb | ||
|
cfa9b6e13a | ||
|
e26d5ca923 | ||
|
dd9d00d293 | ||
|
89a52d6280 | ||
|
e6520c0270 | ||
|
913a38111f | ||
|
4847149b6e | ||
|
d7573fe584 | ||
|
cb74c0cfe4 | ||
|
b725924f0a | ||
|
81cefc1913 | ||
|
a07cfee644 | ||
|
13cf92df27 | ||
|
61e6275781 | ||
|
78d772af86 | ||
|
e9e475a29d | ||
|
20d1be18af | ||
|
b0664a5e6c | ||
|
a38dbd9c8a | ||
|
f6a8d835d3 | ||
|
4746feaa1c | ||
|
3d4e788ea9 | ||
|
bd40574476 | ||
|
1674e2f34c | ||
|
3b2e783c1f | ||
|
46a9a23749 | ||
|
4e929b2d17 | ||
|
ef44c62d17 | ||
|
219aac7800 | ||
|
c110fa62ac | ||
|
7a6eaad445 | ||
|
460e380d38 | ||
|
778b37790b | ||
|
b66ec3bf95 | ||
|
51d760960c | ||
|
9110db41c5 | ||
|
45feb439bd | ||
|
44829d8216 | ||
|
49092945ab | ||
|
c82a2358bd | ||
|
ecf06d7e82 | ||
|
42fe05dea1 | ||
|
b4f8e87358 | ||
|
e72db6d9dd | ||
|
036dd98abb | ||
|
7901f9f63e | ||
|
0963b6fd22 | ||
|
379cdfaac5 | ||
|
38b9af76a2 | ||
|
e4db0f28d2 | ||
|
e7d741ece3 | ||
|
ecd36c1ede | ||
|
64f2ada5d4 | ||
|
a3c4138197 | ||
|
51b7a22ea7 | ||
|
68218d97c8 | ||
|
5131012505 | ||
|
473a69ab18 | ||
|
fce8464077 | ||
|
47bdb9b33b | ||
|
e852872846 | ||
|
41a01bec23 | ||
|
4072b68686 | ||
|
6f5f434caa | ||
|
76198c63b6 | ||
|
7150f2e9d3 | ||
|
3a6ace4874 | ||
|
22a441e374 | ||
|
a40167cf4d | ||
|
18513a978a | ||
|
c33931b613 | ||
|
5cc716688a | ||
|
f0a1b1a152 | ||
|
2e8a492e88 | ||
|
7cb49eaa3a | ||
|
4d8c0d9959 | ||
|
f8f0572ee0 | ||
|
e668180044 | ||
|
8fa924e372 | ||
|
3e46f12340 | ||
|
3084fe4959 | ||
|
b8535ad4df | ||
|
5f3bee345d | ||
|
755aad534a | ||
|
c71aa468b5 | ||
|
d8bc64bb09 | ||
|
90f12f2e5a | ||
|
d3a62d2637 | ||
|
4bc625166e | ||
|
61ed133fea | ||
|
c1e77b56a9 | ||
|
f69d7cb43b | ||
|
a7171af0a3 | ||
|
a4fd4ad1d5 | ||
|
02856073f7 | ||
|
be9bab171d | ||
|
7124881273 | ||
|
66105929e0 | ||
|
cbb69d41f6 | ||
|
bb26cdda24 | ||
|
78936461d7 | ||
|
bc6751ecce | ||
|
51869f2a8c | ||
|
cba2897108 | ||
|
9b8a448477 | ||
|
a71af98401 | ||
|
a7c50c7aba | ||
|
c770b503c0 | ||
|
ffdf0f2ff6 | ||
|
eb3262b941 | ||
|
9dbae6e8a1 | ||
|
1122579216 | ||
|
478ca39e5e | ||
|
f7765acf9d | ||
|
ecdac9017e | ||
|
ba8ec4eed6 | ||
|
6ef3874b2e | ||
|
e20700fe8f | ||
|
cf36d184f4 | ||
|
718802a05d | ||
|
411c9ecb4b | ||
|
cbe8743e47 | ||
|
3ebc0ad4d3 | ||
|
235c14c79d | ||
|
2ef9d0e101 | ||
|
76f3d5d16b | ||
|
1167c6dbf8 | ||
|
298c81c00f | ||
|
cf32f7da5c | ||
|
2bb393684b | ||
|
67f7ffa792 | ||
|
95c8232109 | ||
|
38e0133e1b | ||
|
9b6223f5e2 | ||
|
3f35d43222 | ||
|
c156a83e7d | ||
|
258dcb849f | ||
|
4e4f1b0dcb | ||
|
26f21fd5a0 | ||
|
9da81a1639 | ||
|
d75d2a9f99 | ||
|
f7bf36d8fc | ||
|
33f56811e3 | ||
|
7e5c433dfc | ||
|
c1efe0aa1d | ||
|
ac1093256c | ||
|
af40824998 | ||
|
77dd9e7d27 | ||
|
5da5c65db8 | ||
|
0be9a1e321 | ||
|
8e4cf6282b | ||
|
04fef7b888 | ||
|
1afc70c990 | ||
|
f4bd51da1e | ||
|
ffb2b8ef8c | ||
|
3ed194b67d | ||
|
2cff744cdf | ||
|
e14c20582f | ||
|
47eda1e5fb | ||
|
97dcfb0f50 | ||
|
79bc3d5845 | ||
|
106efba800 | ||
|
bd8d8ad894 | ||
|
7e07e61a30 | ||
|
4a974c6db1 | ||
|
a3c0a20373 | ||
|
8cd2828e91 | ||
|
3d881eed27 | ||
|
7650506b39 | ||
|
e6db3427b7 | ||
|
daefbd66a6 | ||
|
b1daa71da5 | ||
|
1cc44cba81 | ||
|
4ec9d8b4d9 | ||
|
d966878e87 | ||
|
2fc2725076 | ||
|
69f13e7bca | ||
|
613e7c7521 | ||
|
17cecd75ca | ||
|
8cc65cde27 | ||
|
b7f6ddeaf1 | ||
|
143fb54ab9 | ||
|
48cd6dc6ca | ||
|
cfd2b06821 | ||
|
d613dda91d | ||
|
112b1fa265 | ||
|
31d1485887 | ||
|
1287de1b83 | ||
|
72f9eab3d6 | ||
|
0b7a0d15c7 | ||
|
80b3ca0f6f | ||
|
45afdf1781 | ||
|
79b34a0fa2 | ||
|
872a0d5bd8 | ||
|
01421999ae | ||
|
0b888acfd4 | ||
|
238de58e65 | ||
|
7233ac07d2 | ||
|
b1e03197fa | ||
|
7be53a10b0 | ||
|
a0de3222dd | ||
|
540b3f37ae | ||
|
852b48295f | ||
|
9b3b40df66 | ||
|
d799921c75 | ||
|
e56404be41 | ||
|
7badad7797 | ||
|
59797ee233 | ||
|
fbe7756da6 | ||
|
0a103c7749 | ||
|
fb16c37d2a | ||
|
6f244ba82c | ||
|
ea75ae2d1f | ||
|
acb982fc66 | ||
|
eed7484cd6 | ||
|
02194838dd | ||
|
3323b4173e | ||
|
9a28052e92 | ||
|
e6fd4bea35 | ||
|
5276c0a090 | ||
|
7861c5f108 | ||
|
3987bd18a4 | ||
|
74c1c9ec01 | ||
|
537d2939b1 | ||
|
2091ae92be | ||
|
dcc614f869 | ||
|
ed867eca9d | ||
|
08e4c78e78 | ||
|
704053d221 | ||
|
35b84985a8 | ||
|
d41f0b66cc | ||
|
921b781909 | ||
|
6f5c0afe93 | ||
|
eec6095e02 | ||
|
9f04b0d4b1 | ||
|
628358aeea | ||
|
c235711ffe | ||
|
ff6ca8bdc6 | ||
|
dbda87c31f | ||
|
e4a241abef | ||
|
93555182c3 | ||
|
0eff42d688 | ||
|
1d92b90be9 | ||
|
da809f9eec | ||
|
c4d36d024c | ||
|
9e97fbf0af | ||
|
10f6793fd0 | ||
|
a594139115 | ||
|
95bd85d9e8 | ||
|
8d51ce4290 | ||
|
06636c6eca | ||
|
e9822a4e4e | ||
|
9a61b0ef22 | ||
|
d872902997 | ||
|
5ec25ff3e1 | ||
|
49e296e1b0 | ||
|
7347d4f8bb | ||
|
7571c37c99 | ||
|
3c18964256 | ||
|
c61dd918a2 | ||
|
02ba03d6db | ||
|
3bee0996c5 | ||
|
89daeb43a8 | ||
|
7d4f4f9aab | ||
|
256c2b1de0 | ||
|
02e3e1ec09 | ||
|
ff924f95bb | ||
|
c10f4bdb03 | ||
|
d907d4352e | ||
|
a8b51124ba | ||
|
161c72d66d | ||
|
53d99ebf4f | ||
|
1001922156 | ||
|
99f962ba73 | ||
|
2471796d75 | ||
|
545095b3ce | ||
|
d319b3dbe4 | ||
|
d60fd87e01 | ||
|
94230fe565 | ||
|
04ecf44c2f | ||
|
b6af88192f | ||
|
1419f656e2 | ||
|
3ba7cde38d | ||
|
ce854ed506 | ||
|
21b9da6418 | ||
|
764f876953 | ||
|
2c1ed5f872 | ||
|
7d376e41be | ||
|
f4b80e6511 | ||
|
a56c4742d3 | ||
|
38fc1b498d | ||
|
511c6f9625 | ||
|
868568d1c1 | ||
|
65f30f65a2 | ||
|
e0ef7f9d79 | ||
|
127bfda521 | ||
|
1494509468 | ||
|
1e5d1fa5c8 | ||
|
a3b369337f | ||
|
43c37a4768 | ||
|
cafe27fb29 | ||
|
7e6214b869 | ||
|
a8eb0bf44f | ||
|
35fdf561be | ||
|
081956742c | ||
|
8528fd89d2 | ||
|
9592b5e31e | ||
|
cea98e0c12 | ||
|
6eb60260b1 | ||
|
81d29e4126 | ||
|
c11a52d888 | ||
|
e52293482e | ||
|
f38e6a14f2 | ||
|
a434d9c0cc | ||
|
a29432f0cd | ||
|
098c7d27fe | ||
|
3d3b403359 | ||
|
25b0d7538e | ||
|
a3b2ea599d | ||
|
573414f728 | ||
|
aa273a2718 | ||
|
0d3ffa691e | ||
|
5ad45552b3 | ||
|
dc313f27bb | ||
|
7cad926401 | ||
|
3487460f00 | ||
|
72314d26ae | ||
|
cc75d47926 | ||
|
8bf4cc72b6 | ||
|
ad941f5a21 | ||
|
0aeec0390b | ||
|
fef6625496 | ||
|
775c3056b6 | ||
|
ccf4f170de | ||
|
90e7da16a0 | ||
|
ad75ec8b5b | ||
|
57fcc21a86 | ||
|
6855baa0c5 | ||
|
07b4427865 | ||
|
a8deb6648b | ||
|
20a6584d2d | ||
|
155e211dd0 | ||
|
81923f88ba | ||
|
5706fe18c2 | ||
|
71965cbef2 | ||
|
0128b86d30 | ||
|
0370ba7b0a | ||
|
c986218c3a | ||
|
0c8b1eb577 | ||
|
cfa3f55221 | ||
|
f9f6918148 | ||
|
2a61b9f000 | ||
|
cfea28216f | ||
|
19257d91bf | ||
|
fe180f18ff | ||
|
1486fd64cc | ||
|
14c4a33cd9 | ||
|
30d2ea03b0 | ||
|
1356ed72cd | ||
|
481fac7c84 | ||
|
c588fcf4bc | ||
|
feed07227b | ||
|
e56323a4dd | ||
|
84d5bfb35e | ||
|
6a82939adb | ||
|
98aa96b8d6 | ||
|
3caec1ecc2 | ||
|
2950de86c6 | ||
|
7d4ebeecbd | ||
|
6e3f176b8e | ||
|
a4710f9af8 | ||
|
fcc0795a40 | ||
|
0f8140d26a | ||
|
e7d55df38d | ||
|
a72d03f43c | ||
|
4bce376fdc | ||
|
a865b62efc | ||
|
84cebad49d | ||
|
931e66e572 | ||
|
cdae7e4c8b | ||
|
3a52c90de1 | ||
|
17e26f8afe | ||
|
2526ef10c2 | ||
|
99242b92bc | ||
|
ec3b449baa | ||
|
2f4c5f504f | ||
|
f08e6e9ab5 | ||
|
86b4d5439c | ||
|
c36b9cc5a6 | ||
|
70ce2a2095 | ||
|
b0db4dad79 | ||
|
dad0a09675 | ||
|
bca9e2e57a | ||
|
369f40bb9f | ||
|
65e0bbd958 | ||
|
832a7f9a05 | ||
|
7fcf15adf3 | ||
|
a1fc626e57 | ||
|
9a6fc03332 | ||
|
7445f17571 | ||
|
0c4ca3e549 | ||
|
c083816c24 | ||
|
432761f375 | ||
|
9302369aa5 | ||
|
a0047fdca0 | ||
|
a20509b41e | ||
|
281c577cf8 | ||
|
f9a0d8f2b9 | ||
|
4de211b80a | ||
|
063a1c2a8b | ||
|
a9ca5ce920 | ||
|
d7a17b5e8b | ||
|
34e2a06de0 | ||
|
4c1a02fa73 | ||
|
b21db9bbde | ||
|
42bcbd36b7 | ||
|
0393a64a90 | ||
|
d68868ca14 | ||
|
e20895f251 | ||
|
12cea76634 | ||
|
b4bc594c5a | ||
|
82884ac5c4 | ||
|
886829e96c | ||
|
62a94ebed4 | ||
|
ac17309faf | ||
|
dd23ae031f | ||
|
51f2eca887 | ||
|
bdf6d0a684 | ||
|
b15482ce71 | ||
|
74320971e2 | ||
|
eee3b32b77 | ||
|
df03042a6e | ||
|
9927df83ad | ||
|
4c6b5dbe96 | ||
|
85e97ecab6 | ||
|
dc1ebd45a3 | ||
|
f0d4c7d7ab | ||
|
82ab9736d5 | ||
|
a62039df27 | ||
|
15fab79cfa | ||
|
eeaec39888 | ||
|
b8efb5daed | ||
|
2b3b44ebbc | ||
|
1b57d4dd3a | ||
|
d937a59997 | ||
|
706e534455 | ||
|
ff78c1177a | ||
|
c6b7c77229 | ||
|
e20258a2e5 | ||
|
7fb850e987 | ||
|
1c5b0e3334 | ||
|
740f8a95a9 | ||
|
0ea4478b68 | ||
|
fd87e5a53b | ||
|
57fe4102ea | ||
|
bf7757cbbc | ||
|
1266c66f79 | ||
|
d07983b56d | ||
|
662b8eefe8 | ||
|
520d147803 | ||
|
32987004c9 | ||
|
31ac5f0e00 | ||
|
269a445c0b | ||
|
2b51b4094c | ||
|
1104ac35d3 | ||
|
a78f66c069 | ||
|
8c0e77d688 | ||
|
7a45d382ea | ||
|
5a551b530a | ||
|
e84fecb7e9 | ||
|
801eee0ff3 | ||
|
bc4a726c24 | ||
|
fc2155019b | ||
|
53b7b81b43 | ||
|
6f609dc4b4 | ||
|
3dce6cbbd7 | ||
|
2bcc81700c | ||
|
53e95c4efc | ||
|
08deec4c84 | ||
|
2590aac863 | ||
|
3d1d3d9a20 | ||
|
8f638a2bf2 | ||
|
9d9b1aff1e | ||
|
bfdcf76a64 | ||
|
b380e9d2cb | ||
|
58cede4808 | ||
|
6be72a3ec6 | ||
|
2864e5e077 | ||
|
24cafd73a2 | ||
|
4a2fc2d444 | ||
|
e1ebf36352 | ||
|
ae6dd08121 | ||
|
e28b33c89c | ||
|
dc6e031364 | ||
|
9dd5e329ab | ||
|
3e90987c8b | ||
|
2151fd3150 | ||
|
ad207456d6 | ||
|
9e3d24a150 | ||
|
556c07df1f | ||
|
3023725936 | ||
|
3e4b01b47d | ||
|
19e8b861a2 | ||
|
7d7df877ef | ||
|
c73a1fb537 | ||
|
f6bc6399e2 | ||
|
031a5a8f92 | ||
|
6d7e05ec1f | ||
|
58bca7b1e4 | ||
|
1c25853842 | ||
|
546257bc7f | ||
|
fbef909c2a | ||
|
c3ec1e87b8 | ||
|
48e27c47a7 | ||
|
1f1838420f | ||
|
20150659e6 | ||
|
8087aa83d4 | ||
|
249b0fe107 | ||
|
a6682a3000 | ||
|
4112a0631f | ||
|
0e6c4cb796 | ||
|
92aaa55f06 | ||
|
5df8e30415 | ||
|
60f247c2e7 | ||
|
cf7e840990 | ||
|
252d0fe020 | ||
|
2fb722397d | ||
|
07f7192bc3 | ||
|
fcb9533549 | ||
|
7bb8b0b2fc | ||
|
2b1190065c | ||
|
56720ba590 | ||
|
e5aa4128f6 | ||
|
f9e7336296 | ||
|
07cca6e364 | ||
|
54b42901df | ||
|
d200e041fe | ||
|
49a285ce15 | ||
|
cfd7b7a0b7 | ||
|
36376b5e23 | ||
|
eb97bd8af6 | ||
|
4c0a85ef9b | ||
|
64cc129225 | ||
|
97fc2da2e0 | ||
|
889ada5ee2 | ||
|
3f16caaa50 | ||
|
5d5c0f4f43 | ||
|
1032f3994f | ||
|
cbbeec05be | ||
|
e618edf85a | ||
|
b6e2e999bd | ||
|
782224c991 | ||
|
84cfee2488 | ||
|
7bea1530f4 | ||
|
47b0c61853 | ||
|
864c4d869f | ||
|
d8cd9000d9 | ||
|
d307ee79e9 | ||
|
cf01326cc1 | ||
|
d48779cf7b | ||
|
8a588145d5 | ||
|
8abe9e9058 | ||
|
15c0f6ae56 | ||
|
da3adc0a73 | ||
|
0338c16f9f | ||
|
38d072446b | ||
|
8ae9bd0eea | ||
|
5521e94e24 | ||
|
763a2f8511 | ||
|
60f962eedc | ||
|
47d56438da | ||
|
0692991b54 | ||
|
6705463ed0 | ||
|
a2a4bf4e78 | ||
|
b254e6ca5f | ||
|
29609fbb6a | ||
|
d37a56c07c | ||
|
2cea4592a3 | ||
|
512feab222 | ||
|
5e111ce16d | ||
|
4080569c2d | ||
|
2cbb8e8cd1 | ||
|
3e9236b343 | ||
|
89c77fe225 | ||
|
e843f62f47 | ||
|
ec487166db | ||
|
37b267e2ab | ||
|
3de22a82bf | ||
|
e4080772b5 | ||
|
781105293c | ||
|
0cb329f63a | ||
|
0129f5eada | ||
|
22da775a85 | ||
|
d556be2968 | ||
|
4f337c020a | ||
|
02f7f3619a | ||
|
20fee786b1 | ||
|
74777599cf | ||
|
1ba3725473 | ||
|
fdb0848e08 | ||
|
8392ddbf87 | ||
|
049381b284 | ||
|
09d81defcd | ||
|
3810d98cd8 | ||
|
26b2a6a71e | ||
|
edf9a5e4fc | ||
|
c710069c12 | ||
|
990d6dd565 | ||
|
402da46ff6 | ||
|
e7099d8d9e | ||
|
637ea3bb5b | ||
|
363d0d3a44 | ||
|
6e54719474 | ||
|
f3003417c5 | ||
|
33ea042dec | ||
|
8b22a63ab0 | ||
|
05686cc99d | ||
|
484208ce12 | ||
|
3bc8924940 | ||
|
a02de9e012 | ||
|
2d395324e1 | ||
|
e6c9756fa9 | ||
|
5050719fac | ||
|
989553c69a | ||
|
0e0c6b1b4b | ||
|
554c2fd8af | ||
|
a2b600428c | ||
|
df1a9c5ab5 | ||
|
8980aa804f | ||
|
34118169ac | ||
|
4fd7aebd5e | ||
|
bc89995f65 | ||
|
7cc71748ce | ||
|
aec70b44fc | ||
|
6f490b4bfe | ||
|
03975dbde4 | ||
|
f72936b4e6 | ||
|
3c530d95f6 | ||
|
1e7b3bf625 | ||
|
bf0ee1a25c | ||
|
fa0be3f834 | ||
|
981e20b03a | ||
|
d5b767c374 | ||
|
93b54b8d4b | ||
|
e7ab9bf8b4 | ||
|
894da3dcca | ||
|
3e2f793948 | ||
|
8eb7d30a6c | ||
|
7fe1428cc4 | ||
|
b3b4b5a2eb | ||
|
8125fdc19f | ||
|
ae716a12e1 | ||
|
f63a40e7c2 | ||
|
1bdd694a0a | ||
|
2eab41cd1a | ||
|
c6f76db2e1 | ||
|
2c704ca9c6 | ||
|
eb96aa86a4 | ||
|
c1a2707ecf | ||
|
c35132a738 | ||
|
a1c54220e8 | ||
|
df7dbc41ae | ||
|
b8db386e05 | ||
|
48f7a58799 | ||
|
388d093beb | ||
|
95fe20b78a | ||
|
3283868e28 | ||
|
dc91fd482a | ||
|
b8bae96647 | ||
|
d37305c628 | ||
|
ad917cda10 | ||
|
f398ad1994 | ||
|
3cfcc7a50e | ||
|
cb7ba23cd8 | ||
|
691e9112f3 | ||
|
385df2c5a0 | ||
|
35ec1c91e3 | ||
|
9d84b6e606 | ||
|
bf0f7e8846 | ||
|
4817744b87 |
65
.babelrc
65
.babelrc
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"react",
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"loose": true,
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"syntax-dynamic-import",
|
||||
["transform-object-rest-spread", { "useBuiltIns": true }],
|
||||
"transform-decorators-legacy",
|
||||
"transform-class-properties",
|
||||
[
|
||||
"react-intl",
|
||||
{
|
||||
"messagesDir": "./build/messages"
|
||||
}
|
||||
],
|
||||
"preval"
|
||||
],
|
||||
"env": {
|
||||
"development": {
|
||||
"plugins": [
|
||||
"transform-react-jsx-source",
|
||||
"transform-react-jsx-self"
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
[
|
||||
"transform-react-remove-prop-types",
|
||||
{
|
||||
"mode": "remove",
|
||||
"removeImport": true,
|
||||
"additionalLibraries": [
|
||||
"react-immutable-proptypes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"transform-react-inline-elements",
|
||||
[
|
||||
"transform-runtime",
|
||||
{
|
||||
"helpers": true,
|
||||
"polyfill": false,
|
||||
"regenerator": false
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"plugins": [
|
||||
"transform-es2015-modules-commonjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
https://github.com/heroku/heroku-buildpack-apt
|
||||
https://github.com/Scalingo/ffmpeg-buildpack
|
||||
https://github.com/Scalingo/nodejs-buildpack
|
||||
https://github.com/Scalingo/ruby-buildpack
|
||||
|
215
.circleci/config.yml
Normal file
215
.circleci/config.yml
Normal file
@@ -0,0 +1,215 @@
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.5.1-stretch-node
|
||||
environment: &ruby_environment
|
||||
BUNDLE_APP_CONFIG: ./.bundle/
|
||||
DB_HOST: localhost
|
||||
DB_USER: root
|
||||
RAILS_ENV: test
|
||||
PARALLEL_TEST_PROCESSORS: 4
|
||||
ALLOW_NOPAM: true
|
||||
CONTINUOUS_INTEGRATION: true
|
||||
DISABLE_SIMPLECOV: true
|
||||
PAM_ENABLED: true
|
||||
PAM_DEFAULT_SERVICE: pam_test
|
||||
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||
working_directory: ~/projects/mastodon/
|
||||
|
||||
- &attach_workspace
|
||||
attach_workspace:
|
||||
at: ~/projects/
|
||||
|
||||
- &persist_to_workspace
|
||||
persist_to_workspace:
|
||||
root: ~/projects/
|
||||
paths:
|
||||
- ./mastodon/
|
||||
|
||||
- &restore_ruby_dependencies
|
||||
restore_cache:
|
||||
keys:
|
||||
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
|
||||
- v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
|
||||
- v2-ruby-dependencies-
|
||||
|
||||
- &install_steps
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-node-dependencies-{{ checksum "yarn.lock" }}
|
||||
- v1-node-dependencies-
|
||||
- run: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
key: v1-node-dependencies-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ./node_modules/
|
||||
|
||||
- *persist_to_workspace
|
||||
|
||||
- &install_system_dependencies
|
||||
run:
|
||||
name: Install system dependencies
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler
|
||||
|
||||
- &install_ruby_dependencies
|
||||
steps:
|
||||
- *attach_workspace
|
||||
|
||||
- *install_system_dependencies
|
||||
|
||||
- run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
|
||||
- *restore_ruby_dependencies
|
||||
- run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production && bundle clean
|
||||
- save_cache:
|
||||
key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
|
||||
paths:
|
||||
- ./.bundle/
|
||||
- ./vendor/bundle/
|
||||
- persist_to_workspace:
|
||||
root: ~/projects/
|
||||
paths:
|
||||
- ./mastodon/.bundle/
|
||||
- ./mastodon/vendor/bundle/
|
||||
|
||||
- &test_steps
|
||||
steps:
|
||||
- *attach_workspace
|
||||
|
||||
- *install_system_dependencies
|
||||
- run: sudo apt-get install -y ffmpeg
|
||||
|
||||
- run:
|
||||
name: Prepare Tests
|
||||
command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
|
||||
- run:
|
||||
name: Run Tests
|
||||
command: ./bin/retry bundle exec parallel_test ./spec/ --group-by filesize --type rspec
|
||||
|
||||
jobs:
|
||||
install:
|
||||
<<: *defaults
|
||||
<<: *install_steps
|
||||
|
||||
install-ruby2.5:
|
||||
<<: *defaults
|
||||
<<: *install_ruby_dependencies
|
||||
|
||||
install-ruby2.4:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.4.4-stretch-node
|
||||
environment: *ruby_environment
|
||||
<<: *install_ruby_dependencies
|
||||
|
||||
install-ruby2.3:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.3.7-stretch-node
|
||||
environment: *ruby_environment
|
||||
<<: *install_ruby_dependencies
|
||||
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *install_system_dependencies
|
||||
- run: ./bin/rails assets:precompile
|
||||
- persist_to_workspace:
|
||||
root: ~/projects/
|
||||
paths:
|
||||
- ./mastodon/public/assets
|
||||
- ./mastodon/public/packs-test/
|
||||
|
||||
test-ruby2.5:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.5.1-stretch-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.3-alpine
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
- image: circleci/redis:4.0.9-alpine
|
||||
<<: *test_steps
|
||||
|
||||
test-ruby2.4:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.4.4-stretch-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.3-alpine
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
- image: circleci/redis:4.0.9-alpine
|
||||
<<: *test_steps
|
||||
|
||||
test-ruby2.3:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/ruby:2.3.7-stretch-node
|
||||
environment: *ruby_environment
|
||||
- image: circleci/postgres:10.3-alpine
|
||||
environment:
|
||||
POSTGRES_USER: root
|
||||
- image: circleci/redis:4.0.9-alpine
|
||||
<<: *test_steps
|
||||
|
||||
test-webui:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: circleci/node:8.11.1-stretch
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- run: ./bin/retry yarn test:jest
|
||||
|
||||
check-i18n:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- run: bundle exec i18n-tasks check-normalized
|
||||
- run: bundle exec i18n-tasks unused
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- install
|
||||
- install-ruby2.5:
|
||||
requires:
|
||||
- install
|
||||
- install-ruby2.4:
|
||||
requires:
|
||||
- install
|
||||
- install-ruby2.5
|
||||
- install-ruby2.3:
|
||||
requires:
|
||||
- install
|
||||
- install-ruby2.5
|
||||
- build:
|
||||
requires:
|
||||
- install-ruby2.5
|
||||
- test-ruby2.5:
|
||||
requires:
|
||||
- install-ruby2.5
|
||||
- build
|
||||
- test-ruby2.4:
|
||||
requires:
|
||||
- install-ruby2.4
|
||||
- build
|
||||
- test-ruby2.3:
|
||||
requires:
|
||||
- install-ruby2.3
|
||||
- build
|
||||
- test-webui:
|
||||
requires:
|
||||
- install
|
||||
- check-i18n:
|
||||
requires:
|
||||
- install-ruby2.5
|
@@ -1,21 +1,38 @@
|
||||
engines:
|
||||
version: "2"
|
||||
checks:
|
||||
argument-count:
|
||||
enabled: false
|
||||
complex-logic:
|
||||
enabled: false
|
||||
file-lines:
|
||||
enabled: false
|
||||
method-complexity:
|
||||
enabled: false
|
||||
method-count:
|
||||
enabled: false
|
||||
method-lines:
|
||||
enabled: false
|
||||
nested-control-flow:
|
||||
enabled: false
|
||||
return-statements:
|
||||
enabled: false
|
||||
similar-code:
|
||||
enabled: false
|
||||
identical-code:
|
||||
enabled: false
|
||||
plugins:
|
||||
brakeman:
|
||||
enabled: true
|
||||
bundler-audit:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: false
|
||||
eslint:
|
||||
enabled: true
|
||||
channel: eslint-4
|
||||
rubocop:
|
||||
enabled: true
|
||||
channel: rubocop-0-54
|
||||
scss-lint:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.rb"
|
||||
- "**.js"
|
||||
- "**.scss"
|
||||
exclude_paths:
|
||||
exclude_patterns:
|
||||
- spec/
|
||||
- vendor/asset
|
||||
|
@@ -1,3 +1,4 @@
|
||||
.bundle
|
||||
.env
|
||||
.env.*
|
||||
public/system
|
||||
@@ -11,3 +12,4 @@ vendor/bundle
|
||||
*~
|
||||
postgres
|
||||
redis
|
||||
elasticsearch
|
||||
|
129
.env.nanobox
129
.env.nanobox
@@ -13,11 +13,29 @@ DB_PORT=5432
|
||||
|
||||
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||
|
||||
# Optional ElasticSearch configuration
|
||||
ES_ENABLED=true
|
||||
ES_HOST=$DATA_ELASTIC_HOST
|
||||
ES_PORT=9200
|
||||
|
||||
# Optimizations
|
||||
LD_PRELOAD=/data/lib/libjemalloc.so
|
||||
|
||||
# ImageMagick optimizations
|
||||
MAGICK_TEMPORARY_PATH=/app/tmp
|
||||
MAGICK_MEMORY_LIMIT=128MiB
|
||||
MAGICK_MAP_LIMIT=64MiB
|
||||
MAGICK_TIME_LIMIT=15
|
||||
MAGICK_AREA_LIMIT=16MP
|
||||
MAGICK_WIDTH_LIMIT=8KP
|
||||
MAGICK_HEIGHT_LIMIT=8KP
|
||||
|
||||
# Federation
|
||||
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||
LOCAL_HTTPS=false
|
||||
|
||||
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||
|
||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||
@@ -31,10 +49,20 @@ LOCAL_HTTPS=false
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
|
||||
PAPERCLIP_SECRET=$PAPERCLIP_SECRET
|
||||
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||
OTP_SECRET=$OTP_SECRET
|
||||
|
||||
# VAPID keys (used for push notifications)
|
||||
# You can generate the keys using the following command (first is the private key, second is the public one)
|
||||
# You should only generate this once per instance. If you later decide to change it, all push subscription will
|
||||
# be invalidated, requiring the users to access the website again to resubscribe.
|
||||
#
|
||||
# Generate with `rake mastodon:webpush:generate_vapid_key` task (`nanobox run bundle exec rake mastodon:webpush:generate_vapid_key`)
|
||||
#
|
||||
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||
VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
|
||||
VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY
|
||||
|
||||
# Registrations
|
||||
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||
# SINGLE_USER_MODE=true
|
||||
@@ -62,7 +90,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||
|
||||
#SMTP_TLS=true
|
||||
|
||||
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
|
||||
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
|
||||
@@ -91,8 +119,25 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
# S3_ENDPOINT=
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||
# S3_CLOUDFRONT_HOST=
|
||||
# Swift (optional)
|
||||
# SWIFT_ENABLED=true
|
||||
# SWIFT_USERNAME=
|
||||
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||
# SWIFT_TENANT=
|
||||
# SWIFT_PASSWORD=
|
||||
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||
# issues with token rate-limiting during high load.
|
||||
# SWIFT_AUTH_URL=
|
||||
# SWIFT_CONTAINER=
|
||||
# SWIFT_OBJECT_URL=
|
||||
# SWIFT_REGION=
|
||||
# Defaults to 'default'
|
||||
# SWIFT_DOMAIN_NAME=
|
||||
# Defaults to 60 seconds. Set to 0 to disable
|
||||
# SWIFT_CACHE_TTL=
|
||||
|
||||
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
|
||||
# S3_ALIAS_HOST=
|
||||
|
||||
# Streaming API integration
|
||||
# STREAMING_API_BASE_URL=
|
||||
@@ -103,9 +148,79 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
|
||||
# Cluster number setting for streaming API server.
|
||||
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||
STREAMING_CLUSTER_NUM=1
|
||||
# STREAMING_CLUSTER_NUM=1
|
||||
|
||||
# Docker mastodon user
|
||||
# If you use Docker, you may want to assign UID/GID manually.
|
||||
# UID=1000
|
||||
# GID=1000
|
||||
|
||||
# LDAP authentication (optional)
|
||||
# LDAP_ENABLED=true
|
||||
# LDAP_HOST=localhost
|
||||
# LDAP_PORT=389
|
||||
# LDAP_METHOD=simple_tls
|
||||
# LDAP_BASE=
|
||||
# LDAP_BIND_DN=
|
||||
# LDAP_PASSWORD=
|
||||
# LDAP_UID=cn
|
||||
|
||||
# PAM authentication (optional)
|
||||
# PAM authentication uses for the email generation the "email" pam variable
|
||||
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||
# The pam environment variable "email" is provided by:
|
||||
# https://github.com/devkral/pam_email_extractor
|
||||
# PAM_ENABLED=true
|
||||
# Fallback Suffix for email address generation (nil by default)
|
||||
# PAM_DEFAULT_SUFFIX=pam
|
||||
# Name of the pam service (pam "auth" section is evaluated)
|
||||
# PAM_DEFAULT_SERVICE=rpam
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||
# PAM_CONTROLLED_SERVICE=rpam
|
||||
|
||||
# Global OAuth settings (optional) :
|
||||
# If you have only one strategy, you may want to enable this
|
||||
# OAUTH_REDIRECT_AT_SIGN_IN=true
|
||||
|
||||
# Optional CAS authentication (cf. omniauth-cas) :
|
||||
# CAS_ENABLED=true
|
||||
# CAS_URL=https://sso.myserver.com/
|
||||
# CAS_HOST=sso.myserver.com/
|
||||
# CAS_PORT=443
|
||||
# CAS_SSL=true
|
||||
# CAS_VALIDATE_URL=
|
||||
# CAS_CALLBACK_URL=
|
||||
# CAS_LOGOUT_URL=
|
||||
# CAS_LOGIN_URL=
|
||||
# CAS_UID_FIELD='user'
|
||||
# CAS_CA_PATH=
|
||||
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||
# CAS_UID_KEY='user'
|
||||
# CAS_NAME_KEY='name'
|
||||
# CAS_EMAIL_KEY='email'
|
||||
# CAS_NICKNAME_KEY='nickname'
|
||||
# CAS_FIRST_NAME_KEY='firstname'
|
||||
# CAS_LAST_NAME_KEY='lastname'
|
||||
# CAS_LOCATION_KEY='location'
|
||||
# CAS_IMAGE_KEY='image'
|
||||
# CAS_PHONE_KEY='phone'
|
||||
|
||||
# Optional SAML authentication (cf. omniauth-saml)
|
||||
# SAML_ENABLED=true
|
||||
# SAML_ACS_URL=
|
||||
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/callback
|
||||
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||
# SAML_IDP_CERT=
|
||||
# SAML_IDP_CERT_FINGERPRINT=
|
||||
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||
# SAML_CERT=
|
||||
# SAML_PRIVATE_KEY=
|
||||
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||
|
@@ -9,12 +9,17 @@ DB_USER=postgres
|
||||
DB_NAME=postgres
|
||||
DB_PASS=
|
||||
DB_PORT=5432
|
||||
# Optional ElasticSearch configuration
|
||||
# ES_ENABLED=true
|
||||
# ES_HOST=es
|
||||
# ES_PORT=9200
|
||||
|
||||
# Federation
|
||||
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||
LOCAL_DOMAIN=example.com
|
||||
LOCAL_HTTPS=true
|
||||
LOCAL_DOMAIN=example.com
|
||||
|
||||
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||
|
||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||
@@ -28,7 +33,6 @@ LOCAL_HTTPS=true
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||
PAPERCLIP_SECRET=
|
||||
SECRET_KEY_BASE=
|
||||
OTP_SECRET=
|
||||
|
||||
@@ -57,7 +61,7 @@ VAPID_PUBLIC_KEY=
|
||||
# E-mail configuration
|
||||
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||
SMTP_SERVER=smtp.mailgun.org
|
||||
SMTP_PORT=587
|
||||
@@ -77,9 +81,17 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# PAPERCLIP_ROOT_URL=/system
|
||||
|
||||
# Optional asset host for multi-server setups
|
||||
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
|
||||
# if WEB_DOMAIN is not set. For example, the server may have the
|
||||
# following header field:
|
||||
# Access-Control-Allow-Origin: https://example.com/
|
||||
# CDN_HOST=https://assets.example.com
|
||||
|
||||
# S3 (optional)
|
||||
# The attachment host must allow cross origin request from WEB_DOMAIN or
|
||||
# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the
|
||||
# following header field:
|
||||
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
|
||||
# S3_ENABLED=true
|
||||
# S3_BUCKET=
|
||||
# AWS_ACCESS_KEY_ID=
|
||||
@@ -89,6 +101,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# S3_HOSTNAME=192.168.1.123:9000
|
||||
|
||||
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||
# The attachment host must allow cross origin request - see the description
|
||||
# above.
|
||||
# S3_ENABLED=true
|
||||
# S3_BUCKET=
|
||||
# AWS_ACCESS_KEY_ID=
|
||||
@@ -100,11 +114,15 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# Swift (optional)
|
||||
# The attachment host must allow cross origin request - see the description
|
||||
# above.
|
||||
# SWIFT_ENABLED=true
|
||||
# SWIFT_USERNAME=
|
||||
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||
# SWIFT_TENANT=
|
||||
# SWIFT_PASSWORD=
|
||||
# Some OpenStack V3 providers require PROJECT_ID (optional)
|
||||
# SWIFT_PROJECT_ID=
|
||||
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||
# issues with token rate-limiting during high load.
|
||||
# SWIFT_AUTH_URL=
|
||||
@@ -116,8 +134,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# Defaults to 60 seconds. Set to 0 to disable
|
||||
# SWIFT_CACHE_TTL=
|
||||
|
||||
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||
# S3_CLOUDFRONT_HOST=
|
||||
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
|
||||
# S3_ALIAS_HOST=
|
||||
|
||||
# Streaming API integration
|
||||
# STREAMING_API_BASE_URL=
|
||||
@@ -134,3 +152,81 @@ STREAMING_CLUSTER_NUM=1
|
||||
# If you use Docker, you may want to assign UID/GID manually.
|
||||
# UID=1000
|
||||
# GID=1000
|
||||
|
||||
# LDAP authentication (optional)
|
||||
# LDAP_ENABLED=true
|
||||
# LDAP_HOST=localhost
|
||||
# LDAP_PORT=389
|
||||
# LDAP_METHOD=simple_tls
|
||||
# LDAP_BASE=
|
||||
# LDAP_BIND_DN=
|
||||
# LDAP_PASSWORD=
|
||||
# LDAP_UID=cn
|
||||
# LDAP_SEARCH_FILTER="%{uid}=%{email}"
|
||||
|
||||
# PAM authentication (optional)
|
||||
# PAM authentication uses for the email generation the "email" pam variable
|
||||
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||
# The pam environment variable "email" is provided by:
|
||||
# https://github.com/devkral/pam_email_extractor
|
||||
# PAM_ENABLED=true
|
||||
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
|
||||
# PAM_EMAIL_DOMAIN=example.com
|
||||
# Name of the pam service (pam "auth" section is evaluated)
|
||||
# PAM_DEFAULT_SERVICE=rpam
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||
# PAM_CONTROLLED_SERVICE=rpam
|
||||
|
||||
# Global OAuth settings (optional) :
|
||||
# If you have only one strategy, you may want to enable this
|
||||
# OAUTH_REDIRECT_AT_SIGN_IN=true
|
||||
|
||||
# Optional CAS authentication (cf. omniauth-cas) :
|
||||
# CAS_ENABLED=true
|
||||
# CAS_URL=https://sso.myserver.com/
|
||||
# CAS_HOST=sso.myserver.com/
|
||||
# CAS_PORT=443
|
||||
# CAS_SSL=true
|
||||
# CAS_VALIDATE_URL=
|
||||
# CAS_CALLBACK_URL=
|
||||
# CAS_LOGOUT_URL=
|
||||
# CAS_LOGIN_URL=
|
||||
# CAS_UID_FIELD='user'
|
||||
# CAS_CA_PATH=
|
||||
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||
# CAS_UID_KEY='user'
|
||||
# CAS_NAME_KEY='name'
|
||||
# CAS_EMAIL_KEY='email'
|
||||
# CAS_NICKNAME_KEY='nickname'
|
||||
# CAS_FIRST_NAME_KEY='firstname'
|
||||
# CAS_LAST_NAME_KEY='lastname'
|
||||
# CAS_LOCATION_KEY='location'
|
||||
# CAS_IMAGE_KEY='image'
|
||||
# CAS_PHONE_KEY='phone'
|
||||
|
||||
# Optional SAML authentication (cf. omniauth-saml)
|
||||
# SAML_ENABLED=true
|
||||
# SAML_ACS_URL=
|
||||
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/callback
|
||||
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||
# SAML_IDP_CERT=
|
||||
# SAML_IDP_CERT_FINGERPRINT=
|
||||
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||
# SAML_CERT=
|
||||
# SAML_PRIVATE_KEY=
|
||||
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4"
|
||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||
|
||||
# Use HTTP proxy for outgoing request (optional)
|
||||
# http_proxy=http://gateway.local:8118
|
||||
# Access control for hidden service.
|
||||
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Node.js
|
||||
NODE_ENV=test
|
||||
# Federation
|
||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
LOCAL_HTTPS=true
|
||||
OTP_SECRET=100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4
|
||||
|
@@ -1,2 +1,2 @@
|
||||
VAGRANT=true
|
||||
LOCAL_DOMAIN=mastodon.dev
|
||||
LOCAL_DOMAIN=mastodon.local
|
||||
|
@@ -5,24 +5,34 @@ env:
|
||||
browser: true
|
||||
node: true
|
||||
es6: true
|
||||
jest: true
|
||||
|
||||
globals:
|
||||
ATTACHMENT_HOST: false
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
plugins:
|
||||
- react
|
||||
- jsx-a11y
|
||||
- import
|
||||
- promise
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaFeatures:
|
||||
arrowFunctions: true
|
||||
experimentalObjectRestSpread: true
|
||||
jsx: true
|
||||
destructuring: true
|
||||
modules: true
|
||||
spread: true
|
||||
ecmaVersion: 2018
|
||||
|
||||
settings:
|
||||
import/extensions:
|
||||
- .js
|
||||
import/ignore:
|
||||
- node_modules
|
||||
- \\.(css|scss|json)$
|
||||
|
||||
rules:
|
||||
|
||||
brace-style: warn
|
||||
comma-dangle:
|
||||
- error
|
||||
@@ -101,27 +111,60 @@ rules:
|
||||
react/self-closing-comp: error
|
||||
|
||||
jsx-a11y/accessible-emoji: warn
|
||||
jsx-a11y/alt-text: warn
|
||||
jsx-a11y/anchor-has-content: warn
|
||||
jsx-a11y/anchor-is-valid:
|
||||
- warn
|
||||
- components:
|
||||
- Link
|
||||
- NavLink
|
||||
specialLink:
|
||||
- to
|
||||
aspect:
|
||||
- noHref
|
||||
- invalidHref
|
||||
- preferButton
|
||||
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||
jsx-a11y/aria-props: warn
|
||||
jsx-a11y/aria-proptypes: warn
|
||||
jsx-a11y/aria-role: warn
|
||||
jsx-a11y/aria-unsupported-elements: warn
|
||||
jsx-a11y/heading-has-content: warn
|
||||
jsx-a11y/href-no-hash: warn
|
||||
jsx-a11y/html-has-lang: warn
|
||||
jsx-a11y/iframe-has-title: warn
|
||||
jsx-a11y/img-has-alt: warn
|
||||
jsx-a11y/img-redundant-alt: warn
|
||||
jsx-a11y/interactive-supports-focus: warn
|
||||
jsx-a11y/label-has-for: off
|
||||
jsx-a11y/mouse-events-have-key-events: warn
|
||||
jsx-a11y/no-access-key: warn
|
||||
jsx-a11y/no-distracting-elements: warn
|
||||
jsx-a11y/no-noninteractive-element-interactions:
|
||||
- warn
|
||||
- handlers:
|
||||
- onClick
|
||||
jsx-a11y/no-onchange: warn
|
||||
jsx-a11y/no-redundant-roles: warn
|
||||
jsx-a11y/onclick-has-focus: warn
|
||||
jsx-a11y/onclick-has-role: warn
|
||||
jsx-a11y/no-static-element-interactions:
|
||||
- warn
|
||||
- handlers:
|
||||
- onClick
|
||||
jsx-a11y/role-has-required-aria-props: warn
|
||||
jsx-a11y/role-supports-aria-props: off
|
||||
jsx-a11y/scope: warn
|
||||
jsx-a11y/tabindex-no-positive: warn
|
||||
|
||||
import/extensions:
|
||||
- error
|
||||
- always
|
||||
- js: never
|
||||
import/newline-after-import: error
|
||||
import/no-extraneous-dependencies:
|
||||
- error
|
||||
- devDependencies:
|
||||
- "config/webpack/**"
|
||||
- "app/javascript/mastodon/test_setup.js"
|
||||
- "app/javascript/**/__tests__/**"
|
||||
import/no-unresolved: error
|
||||
import/no-webpack-loader-syntax: error
|
||||
|
||||
promise/catch-or-return: error
|
||||
|
8
CODEOWNERS → .github/CODEOWNERS
vendored
8
CODEOWNERS → .github/CODEOWNERS
vendored
@@ -22,3 +22,11 @@
|
||||
/app/views/user_mailer/*.fr.text.erb @aldarone
|
||||
/config/locales/*.fr.yml @aldarone
|
||||
/config/locales/fr.yml @aldarone
|
||||
|
||||
# Dutch
|
||||
/app/javascript/mastodon/locales/nl.json @jeroenpraat
|
||||
/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat
|
||||
/app/views/user_mailer/*.nl.html.erb @jeroenpraat
|
||||
/app/views/user_mailer/*.nl.text.erb @jeroenpraat
|
||||
/config/locales/*.nl.yml @jeroenpraat
|
||||
/config/locales/nl.yml @jeroenpraat
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: If something isn't working as expected
|
||||
|
||||
---
|
||||
|
||||
<!-- Make sure that you are submitting a new bug that was not previously reported or already fixed -->
|
||||
|
||||
<!-- Please use a concise and distinct title for the issue -->
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
<!-- What should have happened? -->
|
||||
|
||||
### Actual behaviour
|
||||
|
||||
<!-- What happened? -->
|
||||
|
||||
### Steps to reproduce the problem
|
||||
|
||||
<!-- What were you trying to do? -->
|
||||
|
||||
### Specifications
|
||||
|
||||
<!-- What version or commit hash of Mastodon did you find this bug in? -->
|
||||
|
||||
<!-- If a front-end issue, what browser and operating systems were you using? -->
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: I have a suggestion
|
||||
|
||||
---
|
||||
|
||||
<!-- Please use a concise and distinct title for the issue -->
|
||||
|
||||
<!-- Consider: Could it be implemented as a 3rd party app using the REST API instead? -->
|
||||
|
||||
### Pitch
|
||||
|
||||
<!-- Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before -->
|
||||
|
||||
### Motivation
|
||||
|
||||
<!-- Why do you think this feature is needed? Who would benefit from it? -->
|
10
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Support
|
||||
about: Ask for help with your deployment
|
||||
|
||||
---
|
||||
|
||||
We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below:
|
||||
|
||||
- https://discourse.joinmastodon.org
|
||||
- #mastodon on irc.freenode.net
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -36,9 +36,10 @@ config/deploy/*
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Ignore postgres + redis volume optionally created by docker-compose
|
||||
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
|
||||
postgres
|
||||
redis
|
||||
elasticsearch
|
||||
|
||||
# Ignore Apple files
|
||||
.DS_Store
|
||||
|
17
.rubocop.yml
17
.rubocop.yml
@@ -11,6 +11,7 @@ AllCops:
|
||||
- 'Vagrantfile'
|
||||
- 'vendor/**/*'
|
||||
- 'lib/json_ld/*'
|
||||
- 'lib/templates/**/*'
|
||||
|
||||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
@@ -61,6 +62,9 @@ Metrics/ParameterLists:
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 20
|
||||
|
||||
Naming/MemoizedInstanceVariableName:
|
||||
Enabled: false
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
@@ -70,6 +74,14 @@ Rails/HasAndBelongsToMany:
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Rails/HttpStatus:
|
||||
Enabled: false
|
||||
|
||||
Rails/Exit:
|
||||
Exclude:
|
||||
- 'lib/mastodon/*'
|
||||
- 'lib/cli'
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
@@ -107,5 +119,8 @@ Style/RegexpLiteral:
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
Style/TrailingCommaInLiteral:
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
@@ -1 +1 @@
|
||||
2.4.2
|
||||
2.5.3
|
||||
|
57
.travis.yml
57
.travis.yml
@@ -1,57 +0,0 @@
|
||||
language: ruby
|
||||
cache:
|
||||
bundler: true
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- public/assets
|
||||
- public/packs-test
|
||||
- tmp/cache/babel-loader
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
- LOCAL_HTTPS=true
|
||||
- RAILS_ENV=test
|
||||
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
||||
- PARALLEL_TEST_PROCESSORS=2
|
||||
- "PATH=$HOME:$PATH"
|
||||
|
||||
addons:
|
||||
postgresql: 9.4
|
||||
apt:
|
||||
sources:
|
||||
- trusty-media
|
||||
packages:
|
||||
- ffmpeg
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
- libicu-dev
|
||||
|
||||
rvm:
|
||||
- 2.3.4
|
||||
- 2.4.2
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
install:
|
||||
- nvm install
|
||||
- npm install -g yarn
|
||||
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
|
||||
- bundle exec rails assets:precompile
|
||||
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
|
||||
|
||||
script:
|
||||
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||
- npm test
|
||||
- bundle exec i18n-tasks unused
|
564
AUTHORS.md
Normal file
564
AUTHORS.md
Normal file
@@ -0,0 +1,564 @@
|
||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
||||
and provided thanks to the work of the following contributors:
|
||||
|
||||
* [Gargron](https://github.com/Gargron)
|
||||
* [ykzts](https://github.com/ykzts)
|
||||
* [akihikodaki](https://github.com/akihikodaki)
|
||||
* [mjankowski](https://github.com/mjankowski)
|
||||
* [ThibG](https://github.com/ThibG)
|
||||
* [unarist](https://github.com/unarist)
|
||||
* [m4sk1n](https://github.com/m4sk1n)
|
||||
* [yiskah](https://github.com/yiskah)
|
||||
* [nolanlawson](https://github.com/nolanlawson)
|
||||
* [sorin-davidoi](https://github.com/sorin-davidoi)
|
||||
* [abcang](https://github.com/abcang)
|
||||
* [lynlynlynx](https://github.com/lynlynlynx)
|
||||
* [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* [alpaca-tc](https://github.com/alpaca-tc)
|
||||
* [nclm](https://github.com/nclm)
|
||||
* [ineffyble](https://github.com/ineffyble)
|
||||
* [renatolond](https://github.com/renatolond)
|
||||
* [jeroenpraat](https://github.com/jeroenpraat)
|
||||
* [mayaeh](https://github.com/mayaeh)
|
||||
* [blackle](https://github.com/blackle)
|
||||
* [Quent-in](https://github.com/Quent-in)
|
||||
* [JantsoP](https://github.com/JantsoP)
|
||||
* [nullkal](https://github.com/nullkal)
|
||||
* [yookoala](https://github.com/yookoala)
|
||||
* [ysksn](https://github.com/ysksn)
|
||||
* [shuheiktgw](https://github.com/shuheiktgw)
|
||||
* [ashfurrow](https://github.com/ashfurrow)
|
||||
* [mabkenar](https://github.com/mabkenar)
|
||||
* [zunda](https://github.com/zunda)
|
||||
* [Kjwon15](https://github.com/Kjwon15)
|
||||
* [eramdam](https://github.com/eramdam)
|
||||
* [masarakki](https://github.com/masarakki)
|
||||
* [ticky](https://github.com/ticky)
|
||||
* [takayamaki](https://github.com/takayamaki)
|
||||
* [Quenty31](https://github.com/Quenty31)
|
||||
* [danhunsaker](https://github.com/danhunsaker)
|
||||
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
|
||||
* [hcmiya](https://github.com/hcmiya)
|
||||
* [stephenburgess8](https://github.com/stephenburgess8)
|
||||
* [Wonderfall](https://github.com/Wonderfall)
|
||||
* [matteoaquila](https://github.com/matteoaquila)
|
||||
* [rkarabut](https://github.com/rkarabut)
|
||||
* [yukimochi](https://github.com/yukimochi)
|
||||
* [Artoria2e5](https://github.com/Artoria2e5)
|
||||
* [marrus-sh](https://github.com/marrus-sh)
|
||||
* [krainboltgreene](https://github.com/krainboltgreene)
|
||||
* [patf](https://github.com/patf)
|
||||
* [Aldarone](https://github.com/Aldarone)
|
||||
* [BoFFire](https://github.com/BoFFire)
|
||||
* [clworld](https://github.com/clworld)
|
||||
* [dracos](https://github.com/dracos)
|
||||
* [SerCom_KC](mailto:sercom-kc@users.noreply.github.com)
|
||||
* [Sylvhem](https://github.com/Sylvhem)
|
||||
* [nightpool](https://github.com/nightpool)
|
||||
* [MasterGroosha](https://github.com/MasterGroosha)
|
||||
* [JeanGauthier](https://github.com/JeanGauthier)
|
||||
* [kschaper](https://github.com/kschaper)
|
||||
* [MaciekBaron](https://github.com/MaciekBaron)
|
||||
* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
|
||||
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
|
||||
* [adbelle](https://github.com/adbelle)
|
||||
* [evanminto](https://github.com/evanminto)
|
||||
* [MightyPork](https://github.com/MightyPork)
|
||||
* [yhirano55](https://github.com/yhirano55)
|
||||
* [camponez](https://github.com/camponez)
|
||||
* [SerCom-KC](https://github.com/SerCom-KC)
|
||||
* [aschmitz](https://github.com/aschmitz)
|
||||
* [devkral](https://github.com/devkral)
|
||||
* [fpiesche](https://github.com/fpiesche)
|
||||
* [gandaro](https://github.com/gandaro)
|
||||
* [johnsudaar](https://github.com/johnsudaar)
|
||||
* [trebmuh](https://github.com/trebmuh)
|
||||
* [Rakib Hasan](mailto:rmhasan@gmail.com)
|
||||
* [lindwurm](https://github.com/lindwurm)
|
||||
* [victorhck](mailto:victorhck@geeko.site)
|
||||
* [voidsatisfaction](https://github.com/voidsatisfaction)
|
||||
* [hikari-no-yume](https://github.com/hikari-no-yume)
|
||||
* [angristan](https://github.com/angristan)
|
||||
* [seefood](https://github.com/seefood)
|
||||
* [jackjennings](https://github.com/jackjennings)
|
||||
* [spla](mailto:spla@mastodont.cat)
|
||||
* [expenses](https://github.com/expenses)
|
||||
* [walf443](https://github.com/walf443)
|
||||
* [JoelQ](https://github.com/JoelQ)
|
||||
* [mistydemeo](https://github.com/mistydemeo)
|
||||
* [dunn](https://github.com/dunn)
|
||||
* [xqus](https://github.com/xqus)
|
||||
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
|
||||
* [fakenine](https://github.com/fakenine)
|
||||
* [tsuwatch](https://github.com/tsuwatch)
|
||||
* [victorhck](https://github.com/victorhck)
|
||||
* [puckipedia](https://github.com/puckipedia)
|
||||
* [fvh-P](https://github.com/fvh-P)
|
||||
* [contraexemplo](https://github.com/contraexemplo)
|
||||
* [hugogameiro](https://github.com/hugogameiro)
|
||||
* [kazu9su](https://github.com/kazu9su)
|
||||
* [Komic](https://github.com/Komic)
|
||||
* [diomed](https://github.com/diomed)
|
||||
* [ariasuni](https://github.com/ariasuni)
|
||||
* [Neetshin](mailto:neetshin@neetsh.in)
|
||||
* [rainyday](https://github.com/rainyday)
|
||||
* [ProgVal](https://github.com/ProgVal)
|
||||
* [valentin2105](https://github.com/valentin2105)
|
||||
* [yuntan](https://github.com/yuntan)
|
||||
* [goofy-bz](mailto:goofy@babelzilla.org)
|
||||
* [kadiix](https://github.com/kadiix)
|
||||
* [kodacs](https://github.com/kodacs)
|
||||
* [rtucker](https://github.com/rtucker)
|
||||
* [KScl](https://github.com/KScl)
|
||||
* [sterdev](https://github.com/sterdev)
|
||||
* [TheKinrar](https://github.com/TheKinrar)
|
||||
* [AA4ch1](https://github.com/AA4ch1)
|
||||
* [alexgleason](https://github.com/alexgleason)
|
||||
* [cpytel](https://github.com/cpytel)
|
||||
* [northerner](https://github.com/northerner)
|
||||
* [fhemberger](https://github.com/fhemberger)
|
||||
* [greysteil](https://github.com/greysteil)
|
||||
* [hnrysmth](https://github.com/hnrysmth)
|
||||
* [d6rkaiz](https://github.com/d6rkaiz)
|
||||
* [JMendyk](https://github.com/JMendyk)
|
||||
* [JohnD28](https://github.com/JohnD28)
|
||||
* [znz](https://github.com/znz)
|
||||
* [Naouak](https://github.com/Naouak)
|
||||
* [reneklacan](https://github.com/reneklacan)
|
||||
* [ekiru](https://github.com/ekiru)
|
||||
* [tcitworld](https://github.com/tcitworld)
|
||||
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
|
||||
* [geta6](https://github.com/geta6)
|
||||
* [happycoloredbanana](https://github.com/happycoloredbanana)
|
||||
* [leopku](https://github.com/leopku)
|
||||
* [SansPseudoFix](https://github.com/SansPseudoFix)
|
||||
* [tomfhowe](https://github.com/tomfhowe)
|
||||
* [noraworld](https://github.com/noraworld)
|
||||
* [theboss](https://github.com/theboss)
|
||||
* [178inaba](https://github.com/178inaba)
|
||||
* [alyssais](https://github.com/alyssais)
|
||||
* [kodnaplakal](https://github.com/kodnaplakal)
|
||||
* [stalker314314](https://github.com/stalker314314)
|
||||
* [huertanix](https://github.com/huertanix)
|
||||
* [genesixx](https://github.com/genesixx)
|
||||
* [halkeye](https://github.com/halkeye)
|
||||
* [hinaloe](https://github.com/hinaloe)
|
||||
* [treby](https://github.com/treby)
|
||||
* [Reverite](https://github.com/Reverite)
|
||||
* [jpdevries](https://github.com/jpdevries)
|
||||
* [00x9d](https://github.com/00x9d)
|
||||
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
|
||||
* [saper](https://github.com/saper)
|
||||
* [nevillepark](https://github.com/nevillepark)
|
||||
* [ornithocoder](https://github.com/ornithocoder)
|
||||
* [pierreozoux](https://github.com/pierreozoux)
|
||||
* [qguv](https://github.com/qguv)
|
||||
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
|
||||
* [harukasan](https://github.com/harukasan)
|
||||
* [stamak](https://github.com/stamak)
|
||||
* [Technowix](mailto:technowix@users.noreply.github.com)
|
||||
* [Eychics](https://github.com/Eychics)
|
||||
* [Thor Harald Johansen](mailto:thj@thj.no)
|
||||
* [0x70b1a5](https://github.com/0x70b1a5)
|
||||
* [gled-rs](https://github.com/gled-rs)
|
||||
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
|
||||
* [R0ckweb](https://github.com/R0ckweb)
|
||||
* [caasi](https://github.com/caasi)
|
||||
* [esetomo](https://github.com/esetomo)
|
||||
* [foxiehkins](https://github.com/foxiehkins)
|
||||
* [hoodie](mailto:hoodiekitten@outlook.com)
|
||||
* [luzi82](https://github.com/luzi82)
|
||||
* [duxovni](https://github.com/duxovni)
|
||||
* [unsmell](https://github.com/unsmell)
|
||||
* [chriswmartin](https://github.com/chriswmartin)
|
||||
* [vahnj](https://github.com/vahnj)
|
||||
* [ikuradon](https://github.com/ikuradon)
|
||||
* [AndreLewin](https://github.com/AndreLewin)
|
||||
* [rinsuki](https://github.com/rinsuki)
|
||||
* [redtachyons](https://github.com/redtachyons)
|
||||
* [thurloat](https://github.com/thurloat)
|
||||
* [aaribaud](https://github.com/aaribaud)
|
||||
* [Andrew](mailto:andrewlchronister@gmail.com)
|
||||
* [estuans](https://github.com/estuans)
|
||||
* [dissolve](https://github.com/dissolve)
|
||||
* [PurpleBooth](https://github.com/PurpleBooth)
|
||||
* [bradurani](https://github.com/bradurani)
|
||||
* [wavebeem](https://github.com/wavebeem)
|
||||
* [bruwalfas](https://github.com/bruwalfas)
|
||||
* [foxsan48](https://github.com/foxsan48)
|
||||
* [wchristian](https://github.com/wchristian)
|
||||
* [muffinista](https://github.com/muffinista)
|
||||
* [cdutson](https://github.com/cdutson)
|
||||
* [farlistener](https://github.com/farlistener)
|
||||
* [DavidLibeau](https://github.com/DavidLibeau)
|
||||
* [SirCmpwn](https://github.com/SirCmpwn)
|
||||
* [Fjoerfoks](https://github.com/Fjoerfoks)
|
||||
* [fmauNeko](https://github.com/fmauNeko)
|
||||
* [gloaec](https://github.com/gloaec)
|
||||
* [unstabler](https://github.com/unstabler)
|
||||
* [potato4d](https://github.com/potato4d)
|
||||
* [h-izumi](https://github.com/h-izumi)
|
||||
* [ErikXXon](https://github.com/ErikXXon)
|
||||
* [ian-kelling](https://github.com/ian-kelling)
|
||||
* [immae](https://github.com/immae)
|
||||
* [foozmeat](https://github.com/foozmeat)
|
||||
* [jasonrhodes](https://github.com/jasonrhodes)
|
||||
* [Jason Snell](mailto:jason@newrelic.com)
|
||||
* [jviide](https://github.com/jviide)
|
||||
* [crakaC](https://github.com/crakaC)
|
||||
* [tkbky](https://github.com/tkbky)
|
||||
* [Kaylee](mailto:kaylee@codethat.sucks)
|
||||
* [Kazhnuz](https://github.com/Kazhnuz)
|
||||
* [connyduck](https://github.com/connyduck)
|
||||
* [Lindsey Bieda](mailto:lindseyb@users.noreply.github.com)
|
||||
* [Lorenz Diener](mailto:halcyon@icosahedron.website)
|
||||
* [alimony](https://github.com/alimony)
|
||||
* [mig5](https://github.com/mig5)
|
||||
* [ndarville](https://github.com/ndarville)
|
||||
* [Abzol](https://github.com/Abzol)
|
||||
* [pwoolcoc](https://github.com/pwoolcoc)
|
||||
* [xPaw](https://github.com/xPaw)
|
||||
* [petzah](https://github.com/petzah)
|
||||
* [ignisf](https://github.com/ignisf)
|
||||
* [raymestalez](https://github.com/raymestalez)
|
||||
* [u1-liquid](https://github.com/u1-liquid)
|
||||
* [sim6](https://github.com/sim6)
|
||||
* [stemid](https://github.com/stemid)
|
||||
* [ThomasLeister](https://github.com/ThomasLeister)
|
||||
* [mcat-ee](https://github.com/mcat-ee)
|
||||
* [tototoshi](https://github.com/tototoshi)
|
||||
* [TrashMacNugget](https://github.com/TrashMacNugget)
|
||||
* [VirtuBox](https://github.com/VirtuBox)
|
||||
* [Vladyslav](mailto:vaden@tuta.io)
|
||||
* [kaniini](https://github.com/kaniini)
|
||||
* [vayan](https://github.com/vayan)
|
||||
* [yannicka](https://github.com/yannicka)
|
||||
* [ikasoumen](https://github.com/ikasoumen)
|
||||
* [zacanger](https://github.com/zacanger)
|
||||
* [amazedkoumei](https://github.com/amazedkoumei)
|
||||
* [anon5r](https://github.com/anon5r)
|
||||
* [aus-social](https://github.com/aus-social)
|
||||
* [imbsky](https://github.com/imbsky)
|
||||
* [bsky](mailto:me@imbsky.net)
|
||||
* [chr-1x](https://github.com/chr-1x)
|
||||
* [codl](https://github.com/codl)
|
||||
* [cpsdqs](https://github.com/cpsdqs)
|
||||
* [barzamin](https://github.com/barzamin)
|
||||
* [fhalna](https://github.com/fhalna)
|
||||
* [haoyayoi](https://github.com/haoyayoi)
|
||||
* [ik11235](https://github.com/ik11235)
|
||||
* [kawax](https://github.com/kawax)
|
||||
* [kedamaDQ](https://github.com/kedamaDQ)
|
||||
* [007lva](https://github.com/007lva)
|
||||
* [matsurai25](https://github.com/matsurai25)
|
||||
* [mecab](https://github.com/mecab)
|
||||
* [nicobz25](https://github.com/nicobz25)
|
||||
* [oliverkeeble](https://github.com/oliverkeeble)
|
||||
* [pinfort](https://github.com/pinfort)
|
||||
* [rbaumert](https://github.com/rbaumert)
|
||||
* [rhoio](https://github.com/rhoio)
|
||||
* [trwnh](https://github.com/trwnh)
|
||||
* [usagi-f](https://github.com/usagi-f)
|
||||
* [vidarlee](https://github.com/vidarlee)
|
||||
* [vjackson725](https://github.com/vjackson725)
|
||||
* [wxcafe](https://github.com/wxcafe)
|
||||
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
|
||||
* [cygnan](https://github.com/cygnan)
|
||||
* [Awea](https://github.com/Awea)
|
||||
* [halcy](https://github.com/halcy)
|
||||
* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf)
|
||||
* [NecroTechno](https://github.com/NecroTechno)
|
||||
* [8398a7](https://github.com/8398a7)
|
||||
* [857b](https://github.com/857b)
|
||||
* [insom](https://github.com/insom)
|
||||
* [Aditoo17](https://github.com/Aditoo17)
|
||||
* [unascribed](https://github.com/unascribed)
|
||||
* [Aguay-val](https://github.com/Aguay-val)
|
||||
* [knu](https://github.com/knu)
|
||||
* [h3poteto](https://github.com/h3poteto)
|
||||
* [unleashed](https://github.com/unleashed)
|
||||
* [alxrcs](https://github.com/alxrcs)
|
||||
* [console-cowboy](https://github.com/console-cowboy)
|
||||
* [pointlessone](https://github.com/pointlessone)
|
||||
* [a2](https://github.com/a2)
|
||||
* [0xa](https://github.com/0xa)
|
||||
* [palindromordnilap](https://github.com/palindromordnilap)
|
||||
* [virtualpain](https://github.com/virtualpain)
|
||||
* [sapphirus](https://github.com/sapphirus)
|
||||
* [amandavisconti](https://github.com/amandavisconti)
|
||||
* [ameliavoncat](https://github.com/ameliavoncat)
|
||||
* [ilpianista](https://github.com/ilpianista)
|
||||
* [Andreas Drop](mailto:andy@remline.de)
|
||||
* [andi1984](https://github.com/andi1984)
|
||||
* [schas002](https://github.com/schas002)
|
||||
* [abackstrom](https://github.com/abackstrom)
|
||||
* [jumbosushi](https://github.com/jumbosushi)
|
||||
* [ayumin](https://github.com/ayumin)
|
||||
* [BaptisteGelez](https://github.com/BaptisteGelez)
|
||||
* [bzg](https://github.com/bzg)
|
||||
* [benediktg](https://github.com/benediktg)
|
||||
* [blakebarnett](https://github.com/blakebarnett)
|
||||
* [bradj](https://github.com/bradj)
|
||||
* [brycied00d](https://github.com/brycied00d)
|
||||
* [carlosjs23](https://github.com/carlosjs23)
|
||||
* [cgxxx](https://github.com/cgxxx)
|
||||
* [kibitan](https://github.com/kibitan)
|
||||
* [chrisheninger](https://github.com/chrisheninger)
|
||||
* [chris-martin](https://github.com/chris-martin)
|
||||
* [DoubleMalt](https://github.com/DoubleMalt)
|
||||
* [Moosh-be](https://github.com/Moosh-be)
|
||||
* [Motoma](https://github.com/Motoma)
|
||||
* [chriswk](https://github.com/chriswk)
|
||||
* [csu](https://github.com/csu)
|
||||
* [kklleemm](https://github.com/kklleemm)
|
||||
* [colindean](https://github.com/colindean)
|
||||
* [dachinat](https://github.com/dachinat)
|
||||
* [multiple-creatures](https://github.com/multiple-creatures)
|
||||
* [watilde](https://github.com/watilde)
|
||||
* [daprice](https://github.com/daprice)
|
||||
* [dar5hak](https://github.com/dar5hak)
|
||||
* [kant](https://github.com/kant)
|
||||
* [maxolasersquad](https://github.com/maxolasersquad)
|
||||
* [singingwolfboy](https://github.com/singingwolfboy)
|
||||
* [davidcelis](https://github.com/davidcelis)
|
||||
* [davefp](https://github.com/davefp)
|
||||
* [yipdw](https://github.com/yipdw)
|
||||
* [debanshuk](https://github.com/debanshuk)
|
||||
* [Derek Lewis](mailto:derekcecillewis@gmail.com)
|
||||
* [dblandin](https://github.com/dblandin)
|
||||
* [Drew Gates](mailto:aranaur@users.noreply.github.com)
|
||||
* [dtschust](https://github.com/dtschust)
|
||||
* [Dryusdan](https://github.com/Dryusdan)
|
||||
* [eai04191](https://github.com/eai04191)
|
||||
* [d3vgru](https://github.com/d3vgru)
|
||||
* [Elizafox](https://github.com/Elizafox)
|
||||
* [ericblade](https://github.com/ericblade)
|
||||
* [mikoim](https://github.com/mikoim)
|
||||
* [espenronnevik](https://github.com/espenronnevik)
|
||||
* [Finariel](https://github.com/Finariel)
|
||||
* [siuying](https://github.com/siuying)
|
||||
* [GenbuHase](https://github.com/GenbuHase)
|
||||
* [hattori6789](https://github.com/hattori6789)
|
||||
* [algernon](https://github.com/algernon)
|
||||
* [Fastbyte01](https://github.com/Fastbyte01)
|
||||
* [Gomasy](https://github.com/Gomasy)
|
||||
* [myfreeweb](https://github.com/myfreeweb)
|
||||
* [gfaivre](https://github.com/gfaivre)
|
||||
* [Fiaxhs](https://github.com/Fiaxhs)
|
||||
* [reedcourty](https://github.com/reedcourty)
|
||||
* [anneau](https://github.com/anneau)
|
||||
* [lanodan](https://github.com/lanodan)
|
||||
* [Harmon758](https://github.com/Harmon758)
|
||||
* [HellPie](https://github.com/HellPie)
|
||||
* [Habu-Kagumba](https://github.com/Habu-Kagumba)
|
||||
* [suzukaze](https://github.com/suzukaze)
|
||||
* [Hiromi-Kai](https://github.com/Hiromi-Kai)
|
||||
* [hishamhm](https://github.com/hishamhm)
|
||||
* [musashino205](https://github.com/musashino205)
|
||||
* [iwaim](https://github.com/iwaim)
|
||||
* [valrus](https://github.com/valrus)
|
||||
* [IMcD23](https://github.com/IMcD23)
|
||||
* [yi0713](https://github.com/yi0713)
|
||||
* [iblech](https://github.com/iblech)
|
||||
* [usbsnowcrash](https://github.com/usbsnowcrash)
|
||||
* [jack-michaud](https://github.com/jack-michaud)
|
||||
* [Floppy](https://github.com/Floppy)
|
||||
* [loomchild](https://github.com/loomchild)
|
||||
* [jenkr55](https://github.com/jenkr55)
|
||||
* [docjkl](https://github.com/docjkl)
|
||||
* [TrollDecker](https://github.com/TrollDecker)
|
||||
* [jmontane](https://github.com/jmontane)
|
||||
* [jonathanklee](https://github.com/jonathanklee)
|
||||
* [jguerder](https://github.com/jguerder)
|
||||
* [Jehops](https://github.com/Jehops)
|
||||
* [joshuap](https://github.com/joshuap)
|
||||
* [YuleZ](https://github.com/YuleZ)
|
||||
* [Tiwy57](https://github.com/Tiwy57)
|
||||
* [xuv](https://github.com/xuv)
|
||||
* [Jnsll](https://github.com/Jnsll)
|
||||
* [j0k3r](https://github.com/j0k3r)
|
||||
* [KEINOS](https://github.com/KEINOS)
|
||||
* [futoase](https://github.com/futoase)
|
||||
* [Pneumaticat](https://github.com/Pneumaticat)
|
||||
* [Kit Redgrave](mailto:qwertyitis@gmail.com)
|
||||
* [Knut Erik](mailto:abjectio@users.noreply.github.com)
|
||||
* [mkody](https://github.com/mkody)
|
||||
* [k0ta0uchi](https://github.com/k0ta0uchi)
|
||||
* [KrzysiekJ](https://github.com/KrzysiekJ)
|
||||
* [leowzukw](https://github.com/leowzukw)
|
||||
* [lmorchard](https://github.com/lmorchard)
|
||||
* [Tak](https://github.com/Tak)
|
||||
* [cacheflow](https://github.com/cacheflow)
|
||||
* [ldidry](https://github.com/ldidry)
|
||||
* [jemus42](https://github.com/jemus42)
|
||||
* [lfuelling](https://github.com/lfuelling)
|
||||
* [Grabacr07](https://github.com/Grabacr07)
|
||||
* [mistermantas](https://github.com/mistermantas)
|
||||
* [mareklach](https://github.com/mareklach)
|
||||
* [wirehack7](https://github.com/wirehack7)
|
||||
* [martymcguire](https://github.com/martymcguire)
|
||||
* [marvinkopf](https://github.com/marvinkopf)
|
||||
* [otsune](https://github.com/otsune)
|
||||
* [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
|
||||
* [matt-auckland](https://github.com/matt-auckland)
|
||||
* [webroo](https://github.com/webroo)
|
||||
* [matthiasbeyer](https://github.com/matthiasbeyer)
|
||||
* [mattjmattj](https://github.com/mattjmattj)
|
||||
* [mtparet](https://github.com/mtparet)
|
||||
* [maximeborges](https://github.com/maximeborges)
|
||||
* [minacle](https://github.com/minacle)
|
||||
* [michaeljdeeb](https://github.com/michaeljdeeb)
|
||||
* [Themimitoof](https://github.com/Themimitoof)
|
||||
* [cyweo](https://github.com/cyweo)
|
||||
* [Midgard](mailto:m1dgard@users.noreply.github.com)
|
||||
* [mike-burns](https://github.com/mike-burns)
|
||||
* [verymilan](https://github.com/verymilan)
|
||||
* [milmazz](https://github.com/milmazz)
|
||||
* [premist](https://github.com/premist)
|
||||
* [Mnkai](https://github.com/Mnkai)
|
||||
* [mitchhentges](https://github.com/mitchhentges)
|
||||
* [moritzheiber](https://github.com/moritzheiber)
|
||||
* [mouse-reeve](https://github.com/mouse-reeve)
|
||||
* [Mozinet-fr](https://github.com/Mozinet-fr)
|
||||
* [lae](https://github.com/lae)
|
||||
* [Nanamachi](https://github.com/Nanamachi)
|
||||
* [orinthe](https://github.com/orinthe)
|
||||
* [Dar13](https://github.com/Dar13)
|
||||
* [ngerakines](https://github.com/ngerakines)
|
||||
* [vonneudeck](https://github.com/vonneudeck)
|
||||
* [Ninetailed](https://github.com/Ninetailed)
|
||||
* [k24](https://github.com/k24)
|
||||
* [noiob](https://github.com/noiob)
|
||||
* [kwaio](https://github.com/kwaio)
|
||||
* [norayr](https://github.com/norayr)
|
||||
* [joyeusenoelle](https://github.com/joyeusenoelle)
|
||||
* [OlivierNicole](https://github.com/OlivierNicole)
|
||||
* [noppa](https://github.com/noppa)
|
||||
* [Otakan951](https://github.com/Otakan951)
|
||||
* [fahy](https://github.com/fahy)
|
||||
* [PatrickRWells](https://github.com/PatrickRWells)
|
||||
* [Pangoraw](https://github.com/Pangoraw)
|
||||
* [peterkeen](https://github.com/peterkeen)
|
||||
* [pgate](https://github.com/pgate)
|
||||
* [remram44](https://github.com/remram44)
|
||||
* [retokromer](https://github.com/retokromer)
|
||||
* [rfwatson](https://github.com/rfwatson)
|
||||
* [rfreebern](https://github.com/rfreebern)
|
||||
* [Ryan Wade](mailto:ryan.wade@protonmail.com)
|
||||
* [sylph01](https://github.com/sylph01)
|
||||
* [S-H-GAMELINKS](https://github.com/S-H-GAMELINKS)
|
||||
* [staticsafe](https://github.com/staticsafe)
|
||||
* [snwh](https://github.com/snwh)
|
||||
* [sts10](https://github.com/sts10)
|
||||
* [sascha-sl](https://github.com/sascha-sl)
|
||||
* [skoji](https://github.com/skoji)
|
||||
* [ScienJus](https://github.com/ScienJus)
|
||||
* [larkinscott](https://github.com/larkinscott)
|
||||
* [imolein](https://github.com/imolein)
|
||||
* [blinry](https://github.com/blinry)
|
||||
* [Noiwex](https://github.com/Noiwex)
|
||||
* [yuki764](https://github.com/yuki764)
|
||||
* [shnjp](https://github.com/shnjp)
|
||||
* [ernix](https://github.com/ernix)
|
||||
* [rosylilly](https://github.com/rosylilly)
|
||||
* [shouko](https://github.com/shouko)
|
||||
* [Sina Mashek](mailto:sina@mashek.xyz)
|
||||
* [sossii](https://github.com/sossii)
|
||||
* [SpankyWorks](https://github.com/SpankyWorks)
|
||||
* [StefOfficiel](mailto:pichard.stephane@free.fr)
|
||||
* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
|
||||
* [Sébastien Santoro](mailto:dereckson@espace-win.org)
|
||||
* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
|
||||
* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
|
||||
* [Takayuki KUSANO](mailto:github@tkusano.jp)
|
||||
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
|
||||
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
|
||||
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
|
||||
* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
|
||||
* [Toby Pinder](mailto:gigitrix@gmail.com)
|
||||
* [Tomonori Murakami](mailto:crosslife777@gmail.com)
|
||||
* [TomoyaShibata](mailto:wind.of.hometown@gmail.com)
|
||||
* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
|
||||
* [Udo Kramer](mailto:optik@fluffel.io)
|
||||
* [Una](mailto:una@unascribed.com)
|
||||
* [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
|
||||
* [Valentin Lorentz](mailto:progval+git@progval.net)
|
||||
* [Vladimir Mincev](mailto:vladimir@canicinteractive.com)
|
||||
* [Waldir Pimenta](mailto:waldyrious@gmail.com)
|
||||
* [Wesley Ellis](mailto:tahnok@gmail.com)
|
||||
* [Wiktor](mailto:wiktor@metacode.biz)
|
||||
* [Wonderfall](mailto:wonderfall@schrodinger.io)
|
||||
* [YDrogen](mailto:ydrogen45@gmail.com)
|
||||
* [YMHuang](mailto:ymhuang@fmbase.tw)
|
||||
* [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com)
|
||||
* [YOU](mailto:stackexchange.you@gmail.com)
|
||||
* [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp)
|
||||
* [Yanaken](mailto:yanakend@gmail.com)
|
||||
* [Yann Klis](mailto:yann.klis@gmail.com)
|
||||
* [Yeechan Lu](mailto:wz.bluesnow@gmail.com)
|
||||
* [Yusuke Abe](mailto:moonset20@gmail.com)
|
||||
* [Zachary Spector](mailto:logicaldash@gmail.com)
|
||||
* [ZiiX](mailto:ziix@users.noreply.github.com)
|
||||
* [asria-jp](mailto:is@alicematic.com)
|
||||
* [ava](mailto:vladooku@users.noreply.github.com)
|
||||
* [benklop](mailto:benklop@gmail.com)
|
||||
* [bsky](mailto:git@imbsky.net)
|
||||
* [caesarologia](mailto:lopesgemelli.1@gmail.com)
|
||||
* [cbayerlein](mailto:c.bayerlein@gmail.com)
|
||||
* [chrolis](mailto:chrolis@users.noreply.github.com)
|
||||
* [cormo](mailto:cormorant2+github@gmail.com)
|
||||
* [d0p1](mailto:dopi-sama@hush.com)
|
||||
* [evilny0](mailto:evilny0@moomoocamp.net)
|
||||
* [febrezo](mailto:felixbrezo@gmail.com)
|
||||
* [fsubal](mailto:fsubal@users.noreply.github.com)
|
||||
* [fusshi-](mailto:dikky1218@users.noreply.github.com)
|
||||
* [gentaro](mailto:gentaroooo@gmail.com)
|
||||
* [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
|
||||
* [haosbvnker](mailto:github@chaosbunker.com)
|
||||
* [isati](mailto:phil@juchnowi.cz)
|
||||
* [jacob](mailto:jacobherringtondeveloper@gmail.com)
|
||||
* [jenn kaplan](mailto:me@jkap.io)
|
||||
* [jirayudech](mailto:jirayudech@gmail.com)
|
||||
* [jooops](mailto:joops@autistici.org)
|
||||
* [jukper](mailto:jukkaperanto@gmail.com)
|
||||
* [jumoru](mailto:jumoru@mailbox.org)
|
||||
* [karlyeurl](mailto:karl.yeurl@gmail.com)
|
||||
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
|
||||
* [kuro5hin](mailto:rusty@kuro5hin.org)
|
||||
* [luzpaz](mailto:luzpaz@users.noreply.github.com)
|
||||
* [maxypy](mailto:maxime@mpigou.fr)
|
||||
* [mhe](mailto:mail@marcus-herrmann.com)
|
||||
* [mimikun](mailto:dzdzble_effort_311@outlook.jp)
|
||||
* [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
|
||||
* [muan](mailto:muan@github.com)
|
||||
* [neetshin](mailto:neetshin@neetsh.in)
|
||||
* [nightpool](mailto:nightpool@users.noreply.github.com)
|
||||
* [rch850](mailto:rich850@gmail.com)
|
||||
* [roikale](mailto:roikale@users.noreply.github.com)
|
||||
* [rysiekpl](mailto:rysiek@hackerspace.pl)
|
||||
* [saturday06](mailto:dyob@lunaport.net)
|
||||
* [scriptjunkie](mailto:scriptjunkie@scriptjunkie.us)
|
||||
* [seekr](mailto:mario.drs@gmail.com)
|
||||
* [sundevour](mailto:31990469+sundevour@users.noreply.github.com)
|
||||
* [syui](mailto:syui@users.noreply.github.com)
|
||||
* [tackeyy](mailto:mailto.takita.yusuke@gmail.com)
|
||||
* [tateisu](mailto:tateisu@gmail.com)
|
||||
* [tmyt](mailto:shigure@refy.net)
|
||||
* [trevDev()](mailto:trev@trevdev.ca)
|
||||
* [utam0k](mailto:k0ma@utam0k.jp)
|
||||
* [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
|
||||
* [walfie](mailto:walfington@gmail.com)
|
||||
* [y-temp4](mailto:y.temp4@gmail.com)
|
||||
* [ymmtmdk](mailto:ymmtmdk@gmail.com)
|
||||
* [yoshipc](mailto:yoooo@yoshipc.net)
|
||||
* [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com)
|
||||
* [ばん](mailto:detteiu0321@gmail.com)
|
||||
* [みたらしだんご](mailto:mitarashidango@users.noreply.github.com)
|
||||
* [りんすき](mailto:6533808+rinsuki@users.noreply.github.com)
|
||||
* [ヨイツの賢狼ホロ | 3rd style](mailto:horo@yoitsu.moe)
|
||||
* [猫吸血鬼ディフリス / 猫ロキP](mailto:deflis@gmail.com)
|
||||
* [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp)
|
||||
* [西小倉宏信](mailto:nishiko@mindia.jp)
|
||||
* [雨宮美羽](mailto:k737566@gmail.com)
|
||||
|
||||
This document is provided for informational purposes only. Since it is only updated once per release, the version you are looking at may be currently out of date. To see the full list of contributors, consider looking at the [git history](https://github.com/tootsuite/mastodon/graphs/contributors) instead.
|
19
Aptfile
19
Aptfile
@@ -5,6 +5,25 @@ libidn11
|
||||
libidn11-dev
|
||||
libpq-dev
|
||||
libprotobuf-dev
|
||||
libssl-dev
|
||||
libxdamage1
|
||||
libxfixes3
|
||||
protobuf-compiler
|
||||
zlib1g-dev
|
||||
libcairo2
|
||||
libcroco3
|
||||
libdatrie1
|
||||
libgdk-pixbuf2.0-0
|
||||
libgraphite2-3
|
||||
libharfbuzz0b
|
||||
libpango-1.0-0
|
||||
libpangocairo-1.0-0
|
||||
libpangoft2-1.0-0
|
||||
libpixman-1-0
|
||||
librsvg2-2
|
||||
libthai-data
|
||||
libthai0
|
||||
libvpx5
|
||||
libxcb-render0
|
||||
libxcb-shm0
|
||||
libxrender1
|
||||
|
107
CHANGELOG.md
Normal file
107
CHANGELOG.md
Normal file
@@ -0,0 +1,107 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
|
||||
- Add link ownership verification (#8703)
|
||||
- Add conversations API (#8832)
|
||||
- Add limit for the number of people that can be followed from one account (#8807)
|
||||
- Add admin setting to customize mascot (#8766)
|
||||
- Add support for more granular ActivityPub audiences from other software, i.e. circles (#8950)
|
||||
- Add option to block all reports from a domain (#8830)
|
||||
- Add user preference to always expand toots marked with content warnings (#8762)
|
||||
- Add user preference to always hide all media (#8569)
|
||||
- Add `force_login` param to OAuth authorize page (#8655)
|
||||
- Add `tootctl accounts backup` (#8642, #8811)
|
||||
- Add `tootctl accounts create` (#8642, #8811)
|
||||
- Add `tootctl accounts cull` (#8642, #8811)
|
||||
- Add `tootctl accounts delete` (#8642, #8811)
|
||||
- Add `tootctl accounts modify` (#8642, #8811)
|
||||
- Add `tootctl accounts refresh` (#8642, #8811)
|
||||
- Add `tootctl feeds build` (#8642, #8811)
|
||||
- Add `tootctl feeds clear` (#8642, #8811)
|
||||
- Add `tootctl settings registrations open` (#8642, #8811)
|
||||
- Add `tootctl settings registrations close` (#8642, #8811)
|
||||
- Add `min_id` param to REST API to support backwards pagination (#8736)
|
||||
- Add a confirmation dialog when hitting reply and the compose box isn't empty (#8893)
|
||||
- Add PostgreSQL disk space growth tracking in PGHero (#8906)
|
||||
- Add button for disabling local account to report quick actions bar (#9024)
|
||||
- Add Czech language (#8594)
|
||||
- Add `Clear-Site-Data` header when logging out (#8627)
|
||||
- Add `same-site` (`lax`) attribute to cookies (#8626)
|
||||
- Add support for styled scrollbars in Firefox Nightly (#8653)
|
||||
- Add highlight to the active tab in web UI profiles (#8673)
|
||||
- Add auto-focus for comment textarea in report modal (#8689)
|
||||
- Add auto-focus for emoji picker's search field (#8688)
|
||||
- Add nginx and systemd templates to `dist/` directory (#8770)
|
||||
- Add support for `/.well-known/change-password` (#8828)
|
||||
- Add option to override FFMPEG binary path (#8855)
|
||||
- Add `dns-prefetch` tag when using different host for assets or uploads (#8942)
|
||||
- Add `description` meta tag (#8941)
|
||||
- Add `Content-Security-Policy` header (#8957)
|
||||
- Add cache for the instance info API (#8765)
|
||||
|
||||
### Changed
|
||||
|
||||
- Change forms design (#8703)
|
||||
- Change reports overview to group by target account (#8674)
|
||||
- Change web UI to show "read more" link on overly long in-stream statuses (#8205)
|
||||
- Change design of direct messages column (#8832, #9022)
|
||||
- Change home timelines to exclude DMs (#8940)
|
||||
- Change list timelines to exclude all replies (#8683)
|
||||
- Change admin accounts UI default sort to most recent (#8813)
|
||||
- Change documentation URL in the UI (#8898)
|
||||
- Change style of success and failure messages (#8973)
|
||||
- Change DM filtering to always allow DMs from staff (#8993)
|
||||
- Change recommended Ruby version to 2.5.3 (#9003)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` (#8832)
|
||||
- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` (#8905)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove "on this device" label in column push settings (#8704)
|
||||
- Remove rake tasks in favour of tootctl commands (#8675)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix remote statuses using instance's default locale if no language given (#8861)
|
||||
- Fix streaming API not exiting when port or socket is unavailable (#9023)
|
||||
- Fix network calls being performed in database transaction in ActivityPub handler (#8951)
|
||||
- Fix dropdown arrow position (#8637)
|
||||
- Fix first element of dropdowns being focused even if not using keyboard (#8679)
|
||||
- Fix tootctl requiring `bundle exec` invocation (#8619)
|
||||
- Fix public pages not using animation preference for avatars (#8614)
|
||||
- Fix OEmbed/OpenGraph cards not understanding relative URLs (#8669)
|
||||
- Fix some dark emojis not having a white outline (#8597)
|
||||
- Fix media description not being displayed in various media modals (#8678)
|
||||
- Fix generated URLs of desktop notifications missing base URL (#8758)
|
||||
- Fix RTL styles (#8764, #8767, #8823, #8897, #9005, #9007, #9018, #9021)
|
||||
- Fix crash in streaming API when tag param missing (#8955)
|
||||
- Fix hotkeys not working when no element is focused (#8998)
|
||||
- Fix some hotkeys not working on detailed status view (#9006)
|
||||
|
||||
## [2.5.2] - 2018-10-12
|
||||
### Security
|
||||
|
||||
- Fix XSS vulnerability (#8959)
|
||||
|
||||
## [2.5.1] - 2018-10-07
|
||||
### Fixed
|
||||
|
||||
- Fix database migrations for PostgreSQL below 9.5 (#8903)
|
||||
- Fix class autoloading issue in ActivityPub Create handler (#8820)
|
||||
- Fix cache statistics not being sent via statsd when statsd enabled (#8831)
|
||||
- Bump puma from 3.11.4 to 3.12.0 (#8883)
|
||||
|
||||
### Security
|
||||
|
||||
- Fix some local images not having their EXIF metadata stripped on upload (#8714)
|
||||
- Fix being able to enable a disabled relay via ActivityPub Accept handler (#8864)
|
||||
- Bump nokogiri from 1.8.4 to 1.8.5 (#8881)
|
||||
- Fix being able to report statuses not belonging to the reported account (#8916)
|
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eugen@zeonfederated.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
@@ -1,51 +1,37 @@
|
||||
CONTRIBUTING
|
||||
Contributing
|
||||
============
|
||||
|
||||
There are three ways in which you can contribute to this repository:
|
||||
Thank you for considering contributing to Mastodon 🐘
|
||||
|
||||
1. By improving the documentation
|
||||
2. By working on the back-end application
|
||||
3. By working on the front-end application
|
||||
You can contribute in the following ways:
|
||||
|
||||
Choosing what to work on in a large open source project is not easy. The list of [GitHub issues](https://github.com/tootsuite/mastodon/issues) may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise. If your addition creates a new feature or setting, or otherwise changes how things work in some substantial way, please remember to submit a correlating pull request to document your changes in the [documentation](http://github.com/tootsuite/documentation).
|
||||
- Finding and reporting bugs
|
||||
- Translating the Mastodon interface into various languages
|
||||
- Contributing code to Mastodon by fixing bugs or implementing features
|
||||
- Improving the documentation
|
||||
|
||||
Below are the guidelines for working on pull requests:
|
||||
## Bug reports
|
||||
|
||||
## General
|
||||
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.
|
||||
|
||||
- 2 spaces indentation
|
||||
## Translations
|
||||
|
||||
You can submit translations via [Weblate](https://weblate.joinmastodon.org/). They are periodically merged into the codebase.
|
||||
|
||||
[](https://weblate.joinmastodon.org/)
|
||||
|
||||
## Pull requests
|
||||
|
||||
Please use clean, concise titles for your pull requests. We use commit squashing, so the final commit in the master branch will carry the title of the pull request.
|
||||
|
||||
The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged. Splitting tasks into multiple smaller pull requests is often preferable.
|
||||
|
||||
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
|
||||
|
||||
- Unit and integration tests (rspec, jest)
|
||||
- Code style rules (rubocop, eslint)
|
||||
- Normalization of locale files (i18n-tasks)
|
||||
|
||||
## Documentation
|
||||
|
||||
- No spelling mistakes
|
||||
- No orthographic mistakes
|
||||
- No Markdown syntax errors
|
||||
|
||||
## Requirements
|
||||
|
||||
- Ruby
|
||||
- Node.js
|
||||
- PostgreSQL
|
||||
- Redis
|
||||
- Nginx (optional)
|
||||
|
||||
## Back-end application
|
||||
|
||||
It is expected that you have a working development environment set up. The development environment includes [rubocop](https://github.com/bbatsov/rubocop), which checks your Ruby code for compliance with our style guide and best practices. Sublime Text, likely like other editors, has a [Rubocop plugin](https://github.com/pderichs/sublime_rubocop) that runs checks on files as you edit them. The codebase also has a test suite.
|
||||
|
||||
* The codebase is not perfect, at the time of writing, but it is expected that you do not introduce new code style violations
|
||||
* The rspec test suite must pass
|
||||
* To the extent that it is possible, verify your changes. In the best case, by adding new tests to the test suite. At the very least, by running the server or console and checking it manually
|
||||
* If you are introducing new strings to the user interface, they must be using localization methods
|
||||
|
||||
If your code has syntax errors that won't let it run, it's a good sign that the pull request isn't ready for submission yet.
|
||||
|
||||
## Front-end application
|
||||
|
||||
It is expected that you have a working development environment set up (see back-end application section). This project includes an ESLint configuration file, with which you can lint your changes.
|
||||
|
||||
* Avoid grave ESLint violations
|
||||
* Verify that your changes work
|
||||
* If you are introducing new strings, they must be using localization methods
|
||||
|
||||
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
|
||||
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/docs](https://source.joinmastodon.org/mastodon/docs).
|
||||
|
50
Dockerfile
50
Dockerfile
@@ -1,14 +1,17 @@
|
||||
FROM ruby:2.4.2-alpine3.6
|
||||
FROM node:8.12.0-alpine as node
|
||||
FROM ruby:2.4.4-alpine3.7
|
||||
|
||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||
description="A GNU Social-compatible microblogging server"
|
||||
description="Your self-hosted, globally interconnected microblogging community"
|
||||
|
||||
ENV UID=991 GID=991 \
|
||||
ARG UID=991
|
||||
ARG GID=991
|
||||
|
||||
ENV PATH=/mastodon/bin:$PATH \
|
||||
RAILS_SERVE_STATIC_FILES=true \
|
||||
RAILS_ENV=production NODE_ENV=production
|
||||
RAILS_ENV=production \
|
||||
NODE_ENV=production
|
||||
|
||||
ARG YARN_VERSION=1.1.0
|
||||
ARG YARN_DOWNLOAD_SHA256=171c1f9ee93c488c0d774ac6e9c72649047c3f896277d88d0f805266519430f3
|
||||
ARG LIBICONV_VERSION=1.15
|
||||
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||
|
||||
@@ -16,6 +19,11 @@ EXPOSE 3000 4000
|
||||
|
||||
WORKDIR /mastodon
|
||||
|
||||
COPY --from=node /usr/local/bin/node /usr/local/bin/node
|
||||
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||
COPY --from=node /usr/local/bin/npm /usr/local/bin/npm
|
||||
COPY --from=node /opt/yarn-* /opt/yarn
|
||||
|
||||
RUN apk -U upgrade \
|
||||
&& apk add -t build-dependencies \
|
||||
build-base \
|
||||
@@ -35,20 +43,14 @@ RUN apk -U upgrade \
|
||||
imagemagick \
|
||||
libidn \
|
||||
libpq \
|
||||
nodejs \
|
||||
nodejs-npm \
|
||||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
tzdata \
|
||||
&& update-ca-certificates \
|
||||
&& mkdir -p /tmp/src /opt \
|
||||
&& wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
||||
&& echo "$YARN_DOWNLOAD_SHA256 *yarn.tar.gz" | sha256sum -c - \
|
||||
&& tar -xzf yarn.tar.gz -C /tmp/src \
|
||||
&& rm yarn.tar.gz \
|
||||
&& mv /tmp/src/yarn-v$YARN_VERSION /opt/yarn \
|
||||
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
|
||||
&& wget -O libiconv.tar.gz "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
|
||||
&& ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg \
|
||||
&& mkdir -p /tmp/src /opt \
|
||||
&& wget -O libiconv.tar.gz "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
|
||||
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
|
||||
&& tar -xzf libiconv.tar.gz -C /tmp/src \
|
||||
&& rm libiconv.tar.gz \
|
||||
@@ -64,15 +66,21 @@ COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
|
||||
|
||||
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
|
||||
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
|
||||
&& yarn --pure-lockfile \
|
||||
&& yarn install --pure-lockfile --ignore-engines \
|
||||
&& yarn cache clean
|
||||
|
||||
RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \
|
||||
&& mkdir -p /mastodon/public/system /mastodon/public/assets /mastodon/public/packs \
|
||||
&& chown -R mastodon:mastodon /mastodon/public
|
||||
|
||||
COPY . /mastodon
|
||||
|
||||
COPY docker_entrypoint.sh /usr/local/bin/run
|
||||
RUN chown -R mastodon:mastodon /mastodon
|
||||
|
||||
RUN chmod +x /usr/local/bin/run
|
||||
VOLUME /mastodon/public/system
|
||||
|
||||
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||
USER mastodon
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/run"]
|
||||
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
139
Gemfile
139
Gemfile
@@ -1,119 +1,146 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 2.3.0', '< 2.5.0'
|
||||
ruby '>= 2.3.0', '< 2.6.0'
|
||||
|
||||
gem 'pkg-config', '~> 1.2'
|
||||
gem 'pkg-config', '~> 1.3'
|
||||
|
||||
gem 'puma', '~> 3.10'
|
||||
gem 'rails', '~> 5.1.4'
|
||||
gem 'uglifier', '~> 3.2'
|
||||
gem 'puma', '~> 3.12'
|
||||
gem 'rails', '~> 5.2.1'
|
||||
gem 'thor', '~> 0.20'
|
||||
|
||||
gem 'hamlit-rails', '~> 0.2'
|
||||
gem 'pg', '~> 0.20'
|
||||
gem 'pghero', '~> 1.7'
|
||||
gem 'dotenv-rails', '~> 2.2'
|
||||
gem 'pg', '~> 1.1'
|
||||
gem 'makara', '~> 0.4'
|
||||
gem 'pghero', '~> 2.2'
|
||||
gem 'dotenv-rails', '~> 2.5'
|
||||
|
||||
gem 'aws-sdk', '~> 2.9'
|
||||
gem 'fog-openstack', '~> 0.1'
|
||||
gem 'paperclip', '~> 5.1'
|
||||
gem 'aws-sdk-s3', '~> 1.21', require: false
|
||||
gem 'fog-core', '~> 2.1'
|
||||
gem 'fog-openstack', '~> 1.0', require: false
|
||||
gem 'paperclip', '~> 6.0'
|
||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||
gem 'streamio-ffmpeg', '~> 3.0'
|
||||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.5'
|
||||
gem 'bootsnap'
|
||||
gem 'bootsnap', '~> 1.3', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.5'
|
||||
gem 'charlock_holmes', '~> 0.7.6'
|
||||
gem 'iso-639'
|
||||
gem 'chewy', '~> 5.0'
|
||||
gem 'cld3', '~> 3.2.0'
|
||||
gem 'devise', '~> 4.2'
|
||||
gem 'devise', '~> 4.5'
|
||||
gem 'devise-two-factor', '~> 3.0'
|
||||
gem 'doorkeeper', '~> 4.2'
|
||||
|
||||
group :pam_authentication, optional: true do
|
||||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
||||
end
|
||||
|
||||
gem 'net-ldap', '~> 0.10'
|
||||
gem 'omniauth-cas', '~> 1.1'
|
||||
gem 'omniauth-saml', '~> 1.10'
|
||||
gem 'omniauth', '~> 1.2'
|
||||
|
||||
gem 'doorkeeper', '~> 5.0'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'goldfinger', '~> 2.0'
|
||||
gem 'fastimage'
|
||||
gem 'goldfinger', '~> 2.1'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'redis-namespace', '~> 1.5'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 2.2'
|
||||
gem 'http', '~> 3.3'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 0.99'
|
||||
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2'
|
||||
gem 'httplog', '~> 1.1'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'kaminari', '~> 1.0'
|
||||
gem 'kaminari', '~> 1.1'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'mime-types', '~> 3.1'
|
||||
gem 'nokogiri', '~> 1.7'
|
||||
gem 'mime-types', '~> 3.2', require: 'mime/types/columnar'
|
||||
gem 'nokogiri', '~> 1.8'
|
||||
gem 'nsa', '~> 0.2'
|
||||
gem 'oj', '~> 3.0'
|
||||
gem 'oj', '~> 3.6'
|
||||
gem 'ostatus2', '~> 2.0'
|
||||
gem 'ox', '~> 2.5'
|
||||
gem 'pundit', '~> 1.1'
|
||||
gem 'rabl', '~> 0.13'
|
||||
gem 'rack-attack', '~> 5.0'
|
||||
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||
gem 'rack-timeout', '~> 0.4'
|
||||
gem 'rails-i18n', '~> 5.0'
|
||||
gem 'ox', '~> 2.10'
|
||||
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
||||
gem 'pundit', '~> 2.0'
|
||||
gem 'premailer-rails'
|
||||
gem 'rack-attack', '~> 5.4'
|
||||
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
|
||||
gem 'rails-i18n', '~> 5.1'
|
||||
gem 'rails-settings-cached', '~> 0.6'
|
||||
gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'redis', '~> 4.0', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||
gem 'rqrcode', '~> 0.10'
|
||||
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
|
||||
gem 'sanitize', '~> 4.4'
|
||||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-scheduler', '~> 2.1'
|
||||
gem 'sanitize', '~> 4.6'
|
||||
gem 'sidekiq', '~> 5.2'
|
||||
gem 'sidekiq-scheduler', '~> 3.0'
|
||||
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||
gem 'sidekiq-bulk', '~>0.1.1'
|
||||
gem 'simple-navigation', '~> 4.0'
|
||||
gem 'simple_form', '~> 3.4'
|
||||
gem 'simple_form', '~> 4.0'
|
||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||
gem 'strong_migrations'
|
||||
gem 'stoplight', '~> 2.1.3'
|
||||
gem 'strong_migrations', '~> 0.3'
|
||||
gem 'tty-command', '~> 0.8', require: false
|
||||
gem 'tty-prompt', '~> 0.17', require: false
|
||||
gem 'twitter-text', '~> 1.14'
|
||||
gem 'tzinfo-data', '~> 1.2017'
|
||||
gem 'webpacker', '~> 3.0'
|
||||
gem 'tzinfo-data', '~> 1.2018'
|
||||
gem 'webpacker', '~> 3.5'
|
||||
gem 'webpush'
|
||||
|
||||
gem 'json-ld-preloaded', '~> 2.2.1'
|
||||
gem 'rdf-normalize', '~> 0.3.1'
|
||||
gem 'json-ld', '~> 2.2'
|
||||
gem 'rdf-normalize', '~> 0.3'
|
||||
|
||||
group :development, :test do
|
||||
gem 'fabrication', '~> 2.16'
|
||||
gem 'fuubar', '~> 2.2'
|
||||
gem 'fabrication', '~> 2.20'
|
||||
gem 'fuubar', '~> 2.3'
|
||||
gem 'i18n-tasks', '~> 0.9', require: false
|
||||
gem 'pry-byebug', '~> 3.6'
|
||||
gem 'pry-rails', '~> 0.3'
|
||||
gem 'rspec-rails', '~> 3.6'
|
||||
gem 'rspec-rails', '~> 3.8'
|
||||
end
|
||||
|
||||
group :production, :test do
|
||||
gem 'private_address_check', '~> 0.5'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara', '~> 2.14'
|
||||
gem 'capybara', '~> 3.9'
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'faker', '~> 1.7'
|
||||
gem 'faker', '~> 1.9'
|
||||
gem 'microformats', '~> 4.0'
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
gem 'rspec-sidekiq', '~> 3.0'
|
||||
gem 'simplecov', '~> 0.14', require: false
|
||||
gem 'webmock', '~> 3.0'
|
||||
gem 'parallel_tests', '~> 2.14'
|
||||
gem 'simplecov', '~> 0.16', require: false
|
||||
gem 'webmock', '~> 3.4'
|
||||
gem 'parallel_tests', '~> 2.23'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'active_record_query_trace', '~> 1.5'
|
||||
gem 'annotate', '~> 2.7'
|
||||
gem 'better_errors', '~> 2.1'
|
||||
gem 'better_errors', '~> 2.5'
|
||||
gem 'binding_of_caller', '~> 0.7'
|
||||
gem 'bullet', '~> 5.5'
|
||||
gem 'bullet', '~> 5.7'
|
||||
gem 'letter_opener', '~> 1.4'
|
||||
gem 'letter_opener_web', '~> 1.3'
|
||||
gem 'rubocop', require: false
|
||||
gem 'brakeman', '~> 4.0', require: false
|
||||
gem 'memory_profiler'
|
||||
gem 'rubocop', '~> 0.59', require: false
|
||||
gem 'brakeman', '~> 4.3', require: false
|
||||
gem 'bundler-audit', '~> 0.6', require: false
|
||||
gem 'scss_lint', '~> 0.53', require: false
|
||||
gem 'scss_lint', '~> 0.57', require: false
|
||||
|
||||
gem 'capistrano', '~> 3.8'
|
||||
gem 'capistrano-rails', '~> 1.2'
|
||||
gem 'capistrano', '~> 3.11'
|
||||
gem 'capistrano-rails', '~> 1.4'
|
||||
gem 'capistrano-rbenv', '~> 2.1'
|
||||
gem 'capistrano-yarn', '~> 2.0'
|
||||
|
||||
gem 'derailed_benchmarks'
|
||||
gem 'stackprof'
|
||||
end
|
||||
|
||||
group :production do
|
||||
gem 'lograge', '~> 0.5'
|
||||
gem 'lograge', '~> 0.10'
|
||||
gem 'redis-rails', '~> 5.0'
|
||||
end
|
||||
|
795
Gemfile.lock
795
Gemfile.lock
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
[Issue text goes here].
|
||||
|
||||
* * * *
|
||||
|
||||
- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
|
||||
- [ ] This bug happens on a [tagged release](https://github.com/tootsuite/mastodon/releases) and not on `master` (If you're a user, don't worry about this).
|
@@ -1,4 +1,4 @@
|
||||
web: PORT=3000 bundle exec puma -C config/puma.rb
|
||||
sidekiq: PORT=3000 bundle exec sidekiq
|
||||
stream: PORT=4000 yarn run start
|
||||
web: env PORT=3000 bundle exec puma -C config/puma.rb
|
||||
sidekiq: env PORT=3000 bundle exec sidekiq
|
||||
stream: env PORT=4000 yarn run start
|
||||
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
|
||||
|
104
README.md
104
README.md
@@ -1,85 +1,95 @@
|
||||

|
||||
========
|
||||
|
||||
[][travis]
|
||||
[][code_climate]
|
||||
[][releases]
|
||||
[][circleci]
|
||||
[][code_climate]
|
||||
[][weblate]
|
||||
[][docker]
|
||||
|
||||
[travis]: https://travis-ci.org/tootsuite/mastodon
|
||||
[releases]: https://github.com/tootsuite/mastodon/releases
|
||||
[circleci]: https://circleci.com/gh/tootsuite/mastodon
|
||||
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
||||
[weblate]: https://weblate.joinmastodon.org/engage/mastodon/
|
||||
[docker]: https://hub.docker.com/r/tootsuite/mastodon/
|
||||
|
||||
Mastodon is a **free, open-source social network server** based on **open web protocols** like ActivityPub and OStatus. The social focus of the project is a viable decentralized alternative to commercial social media silos that returns the control of the content distribution channels to the people. The technical focus of the project is a good user interface, a clean REST API for 3rd party apps and robust anti-abuse tools.
|
||||
Mastodon is a **free, open-source social network server** based on ActivityPub. Follow friends and discover new ones. Publish anything you want: links, pictures, text, video. All servers of Mastodon are interoperable as a federated network, i.e. users on one server can seamlessly communicate with users from another one. This includes non-Mastodon software that also implements ActivityPub!
|
||||
|
||||
Click on the screenshot below to watch a demo of the UI:
|
||||
Click below to **learn more** in a video:
|
||||
|
||||
[][youtube_demo]
|
||||
[][youtube_demo]
|
||||
|
||||
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
||||
[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
|
||||
|
||||
**Ruby on Rails** is used for the back-end, while **React.js** and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
|
||||
## Navigation
|
||||
|
||||
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
|
||||
- [Project homepage 🐘](https://joinmastodon.org)
|
||||
- [Support the development via Patreon][patreon]
|
||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||
- [Blog](https://blog.joinmastodon.org)
|
||||
- [Documentation](https://docs.joinmastodon.org)
|
||||
- [Browse Mastodon servers](https://joinmastodon.org/#getting-started)
|
||||
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
||||
|
||||
[patreon]: https://www.patreon.com/user?u=619786
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [Frequently Asked Questions](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md)
|
||||
- [Use this tool to find Twitter friends on Mastodon](https://bridge.joinmastodon.org)
|
||||
- [API overview](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md)
|
||||
- [List of Mastodon instances](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/List-of-Mastodon-instances.md)
|
||||
- [List of apps](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md)
|
||||
- [List of sponsors](https://joinmastodon.org/sponsors)
|
||||
[patreon]: https://www.patreon.com/mastodon
|
||||
|
||||
## Features
|
||||
|
||||
<img src="https://docs.joinmastodon.org/elephant.svg" align="right" width="30%" />
|
||||
|
||||
**No vendor lock-in: Fully interoperable with any conforming platform**
|
||||
|
||||
It doesn't have to be Mastodon, whatever implements ActivityPub or OStatus is part of the social network!
|
||||
It doesn't have to be Mastodon, whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
|
||||
|
||||
**Real-time timeline updates**
|
||||
**Real-time, chronological timeline updates**
|
||||
|
||||
See the updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
|
||||
|
||||
**Federated thread resolving**
|
||||
|
||||
If someone you follow replies to a user unknown to the server, the server fetches the full thread so you can view it without leaving the UI
|
||||
|
||||
**Media attachments like images and short videos**
|
||||
|
||||
Upload and view images and WebM/MP4 videos attached to the updates. Videos with no audio track are treated like GIFs; normal videos are looped - like vines!
|
||||
|
||||
**Safety and moderation tools**
|
||||
|
||||
Private posts, locked accounts, phrase filtering, muting, blocking and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
|
||||
|
||||
**OAuth2 and a straightforward REST API**
|
||||
|
||||
Mastodon acts as an OAuth2 provider so 3rd party apps can use the API
|
||||
|
||||
**Fast response times**
|
||||
|
||||
Mastodon tries to be as fast and responsive as possible, so all long-running tasks are delegated to background processing
|
||||
|
||||
**Deployable via Docker**
|
||||
|
||||
You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
Please follow the [development guide](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md) from the documentation repository.
|
||||
Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choice!
|
||||
|
||||
## Deployment
|
||||
|
||||
There are guides in the documentation repository for [deploying on various platforms](https://github.com/tootsuite/documentation#running-mastodon).
|
||||
**Tech stack:**
|
||||
|
||||
- **Ruby on Rails** powers the REST API and other web pages
|
||||
- **React.js** and Redux are used for the dynamic parts of the interface
|
||||
- **Node.js** powers the streaming API
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- **PostgreSQL** 9.5+
|
||||
- **Redis**
|
||||
- **Ruby** 2.4+
|
||||
- **Node.js** 8+
|
||||
|
||||
The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/administration/installation/) is available in the documentation.
|
||||
|
||||
A **Vagrant** configuration is included for development purposes.
|
||||
|
||||
## Contributing
|
||||
|
||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository. [Here are the guidelines for code contributions](CONTRIBUTING.md)
|
||||
Mastodon is **free, open source software** licensed under **AGPLv3**.
|
||||
|
||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository, or submit translations using Weblate. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
**IRC channel**: #mastodon on irc.freenode.net
|
||||
|
||||
---
|
||||
## License
|
||||
|
||||
## Extra credits
|
||||
Copyright (C) 2016-2018 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
|
||||
|
||||
The elephant friend illustrations are created by [Dopatwo](https://mastodon.social/@dopatwo)
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
21
Vagrantfile
vendored
21
Vagrantfile
vendored
@@ -12,7 +12,7 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||
|
||||
# Add repo for NodeJS
|
||||
curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
|
||||
|
||||
# Add firewall rule to redirect 80 to PORT and save
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
||||
@@ -39,6 +39,7 @@ sudo apt-get install \
|
||||
libidn11-dev \
|
||||
libprotobuf-dev \
|
||||
libreadline-dev \
|
||||
libpam0g-dev \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
@@ -48,7 +49,7 @@ curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-instal
|
||||
source /home/vagrant/.rvm/scripts/rvm
|
||||
|
||||
# Install Ruby
|
||||
rvm install ruby-$RUBY_VERSION
|
||||
rvm reinstall ruby-$RUBY_VERSION --disable-binary
|
||||
|
||||
# Configure database
|
||||
sudo -u postgres createuser -U postgres vagrant -s
|
||||
@@ -79,11 +80,14 @@ VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||
# Increase the number of CPUs. Uncomment and adjust to
|
||||
# increase performance
|
||||
# vb.customize ["modifyvm", :id, "--cpus", "3"]
|
||||
|
||||
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
||||
# https://github.com/mitchellh/vagrant/issues/1172
|
||||
@@ -96,19 +100,22 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
end
|
||||
|
||||
config.vm.hostname = "mastodon.dev"
|
||||
|
||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||
# access the development site at http://mastodon.dev.
|
||||
# access the development site at http://mastodon.local.
|
||||
# If you change it, also change it in .env.vagrant before provisioning
|
||||
# the vagrant server to update the development build.
|
||||
#
|
||||
# To install:
|
||||
# $ vagrant plugin install vagrant-hostsupdater
|
||||
config.vm.hostname = "mastodon.local"
|
||||
|
||||
if defined?(VagrantPlugins::HostsUpdater)
|
||||
config.vm.network :private_network, ip: "192.168.42.42", nictype: "virtio"
|
||||
config.hostsupdater.remove_on_suspend = false
|
||||
end
|
||||
|
||||
if config.vm.networks.any? { |type, options| type == :private_network }
|
||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp']
|
||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp', 'actimeo=1']
|
||||
else
|
||||
config.vm.synced_folder ".", "/vagrant"
|
||||
end
|
||||
|
61
app/chewy/statuses_index.rb
Normal file
61
app/chewy/statuses_index.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StatusesIndex < Chewy::Index
|
||||
settings index: { refresh_interval: '15m' }, analysis: {
|
||||
filter: {
|
||||
english_stop: {
|
||||
type: 'stop',
|
||||
stopwords: '_english_',
|
||||
},
|
||||
english_stemmer: {
|
||||
type: 'stemmer',
|
||||
language: 'english',
|
||||
},
|
||||
english_possessive_stemmer: {
|
||||
type: 'stemmer',
|
||||
language: 'possessive_english',
|
||||
},
|
||||
},
|
||||
analyzer: {
|
||||
content: {
|
||||
tokenizer: 'uax_url_email',
|
||||
filter: %w(
|
||||
english_possessive_stemmer
|
||||
lowercase
|
||||
asciifolding
|
||||
cjk_width
|
||||
english_stop
|
||||
english_stemmer
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
define_type ::Status.unscoped.without_reblogs do
|
||||
crutch :mentions do |collection|
|
||||
data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||
end
|
||||
|
||||
crutch :favourites do |collection|
|
||||
data = ::Favourite.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id)
|
||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||
end
|
||||
|
||||
crutch :reblogs do |collection|
|
||||
data = ::Status.where(reblog_of_id: collection.map(&:id)).pluck(:reblog_of_id, :account_id)
|
||||
data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
|
||||
end
|
||||
|
||||
root date_detection: false do
|
||||
field :account_id, type: 'long'
|
||||
|
||||
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].join("\n\n") } do
|
||||
field :stemmed, type: 'text', analyzer: 'content'
|
||||
end
|
||||
|
||||
field :searchable_by, type: 'long', value: ->(status, crutches) { status.searchable_by(crutches) }
|
||||
field :created_at, type: 'date'
|
||||
end
|
||||
end
|
||||
end
|
@@ -9,9 +9,13 @@ class AboutController < ApplicationController
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
end
|
||||
|
||||
def more; end
|
||||
def more
|
||||
render layout: 'public'
|
||||
end
|
||||
|
||||
def terms; end
|
||||
def terms
|
||||
render layout: 'public'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -26,12 +30,12 @@ class AboutController < ApplicationController
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'about-body'
|
||||
@body_classes = 'with-modals'
|
||||
end
|
||||
|
||||
def initial_state_params
|
||||
{
|
||||
settings: {},
|
||||
settings: { known_fediverse: Setting.show_known_fediverse_at_about_page },
|
||||
token: current_session&.token,
|
||||
}
|
||||
end
|
||||
|
@@ -1,13 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AccountsController < ApplicationController
|
||||
PAGE_SIZE = 20
|
||||
|
||||
include AccountControllerConcern
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_cache_headers
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@pinned_statuses = []
|
||||
@body_classes = 'with-modals'
|
||||
@pinned_statuses = []
|
||||
@endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
|
||||
|
||||
if current_account && @account.blocking?(current_account)
|
||||
@statuses = []
|
||||
@@ -15,21 +20,31 @@ class AccountsController < ApplicationController
|
||||
end
|
||||
|
||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = filtered_status_page(params)
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@next_url = next_url unless @statuses.empty?
|
||||
|
||||
unless @statuses.empty?
|
||||
@older_url = older_url if @statuses.last.id > filtered_statuses.last.id
|
||||
@newer_url = newer_url if @statuses.first.id < filtered_statuses.first.id
|
||||
end
|
||||
end
|
||||
|
||||
format.atom do
|
||||
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id])
|
||||
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? }))
|
||||
end
|
||||
|
||||
format.rss do
|
||||
@statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status)
|
||||
render xml: RSS::AccountSerializer.render(@account, @statuses)
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: @account,
|
||||
serializer: ActivityPub::ActorSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
skip_session!
|
||||
|
||||
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
|
||||
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -37,7 +52,7 @@ class AccountsController < ApplicationController
|
||||
private
|
||||
|
||||
def show_pinned_statuses?
|
||||
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_id].present?].none?
|
||||
[replies_requested?, media_requested?, params[:max_id].present?, params[:min_id].present?].none?
|
||||
end
|
||||
|
||||
def filtered_statuses
|
||||
@@ -67,13 +82,22 @@ class AccountsController < ApplicationController
|
||||
@account = Account.find_local!(params[:username])
|
||||
end
|
||||
|
||||
def next_url
|
||||
def older_url
|
||||
::Rails.logger.info("older: max_id #{@statuses.last.id}, url #{pagination_url(max_id: @statuses.last.id)}")
|
||||
pagination_url(max_id: @statuses.last.id)
|
||||
end
|
||||
|
||||
def newer_url
|
||||
pagination_url(min_id: @statuses.first.id)
|
||||
end
|
||||
|
||||
def pagination_url(max_id: nil, min_id: nil)
|
||||
if media_requested?
|
||||
short_account_media_url(@account, max_id: @statuses.last.id)
|
||||
short_account_media_url(@account, max_id: max_id, min_id: min_id)
|
||||
elsif replies_requested?
|
||||
short_account_with_replies_url(@account, max_id: @statuses.last.id)
|
||||
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
||||
else
|
||||
short_account_url(@account, max_id: @statuses.last.id)
|
||||
short_account_url(@account, max_id: max_id, min_id: min_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -84,4 +108,12 @@ class AccountsController < ApplicationController
|
||||
def replies_requested?
|
||||
request.path.ends_with?('/with_replies')
|
||||
end
|
||||
|
||||
def filtered_status_page(params)
|
||||
if params[:min_id].present?
|
||||
filtered_statuses.paginate_by_min_id(PAGE_SIZE, params[:min_id]).reverse
|
||||
else
|
||||
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
57
app/controllers/activitypub/collections_controller.rb
Normal file
57
app/controllers/activitypub/collections_controller.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::CollectionsController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_size
|
||||
before_action :set_statuses
|
||||
|
||||
def show
|
||||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json',
|
||||
skip_activities: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = scope_for_collection
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
def set_size
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@account.pinned_statuses.count
|
||||
else
|
||||
raise ActiveRecord::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def scope_for_collection
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@account.statuses.permitted_for(@account, signed_request_account).tap do |scope|
|
||||
scope.merge!(@account.pinned_statuses)
|
||||
end
|
||||
else
|
||||
raise ActiveRecord::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_collection_url(@account, params[:id]),
|
||||
type: :ordered,
|
||||
size: @size,
|
||||
items: @statuses
|
||||
)
|
||||
end
|
||||
end
|
@@ -11,7 +11,7 @@ class ActivityPub::InboxesController < Api::BaseController
|
||||
process_payload
|
||||
head 202
|
||||
else
|
||||
[signature_verification_failure_reason, 401]
|
||||
render plain: signature_verification_failure_reason, status: 401
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ class ActivityPub::InboxesController < Api::BaseController
|
||||
def upgrade_account
|
||||
if signed_request_account.ostatus?
|
||||
signed_request_account.update(last_webfingered_at: nil)
|
||||
ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
|
||||
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
||||
end
|
||||
|
||||
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
|
||||
|
@@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::OutboxesController < Api::BaseController
|
||||
LIMIT = 20
|
||||
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_statuses
|
||||
|
||||
def show
|
||||
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
|
||||
render json: outbox_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
@@ -17,11 +19,47 @@ class ActivityPub::OutboxesController < Api::BaseController
|
||||
end
|
||||
|
||||
def outbox_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_outbox_url(@account),
|
||||
type: :ordered,
|
||||
size: @account.statuses_count,
|
||||
items: @statuses
|
||||
)
|
||||
if page_requested?
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_outbox_url(@account, page_params),
|
||||
type: :ordered,
|
||||
part_of: account_outbox_url(@account),
|
||||
prev: prev_page,
|
||||
next: next_page,
|
||||
items: @statuses
|
||||
)
|
||||
else
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_outbox_url(@account),
|
||||
type: :ordered,
|
||||
size: @account.statuses_count,
|
||||
first: account_outbox_url(@account, page: true),
|
||||
last: account_outbox_url(@account, page: true, min_id: 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def next_page
|
||||
account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
||||
end
|
||||
|
||||
def prev_page
|
||||
account_outbox_url(@account, page: true, min_id: @statuses.first.id) unless @statuses.empty?
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
return unless page_requested?
|
||||
|
||||
@statuses = @account.statuses.permitted_for(@account, signed_request_account)
|
||||
@statuses = params[:min_id].present? ? @statuses.paginate_by_min_id(LIMIT, params[:min_id]).reverse : @statuses.paginate_by_max_id(LIMIT, params[:max_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
params[:page] == 'true'
|
||||
end
|
||||
|
||||
def page_params
|
||||
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
|
||||
end
|
||||
end
|
||||
|
@@ -1,31 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::AccountModerationNotesController < Admin::BaseController
|
||||
def create
|
||||
@account_moderation_note = current_account.account_moderation_notes.new(resource_params)
|
||||
if @account_moderation_note.save
|
||||
@target_account = @account_moderation_note.target_account
|
||||
redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.created_msg')
|
||||
else
|
||||
@account = @account_moderation_note.target_account
|
||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||
render template: 'admin/accounts/show'
|
||||
module Admin
|
||||
class AccountModerationNotesController < BaseController
|
||||
before_action :set_account_moderation_note, only: [:destroy]
|
||||
|
||||
def create
|
||||
authorize AccountModerationNote, :create?
|
||||
|
||||
@account_moderation_note = current_account.account_moderation_notes.new(resource_params)
|
||||
|
||||
if @account_moderation_note.save
|
||||
redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.created_msg')
|
||||
else
|
||||
@account = @account_moderation_note.target_account
|
||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||
|
||||
render template: 'admin/accounts/show'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @account_moderation_note, :destroy?
|
||||
@account_moderation_note.destroy!
|
||||
redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:account_moderation_note).permit(
|
||||
:content,
|
||||
:target_account_id
|
||||
)
|
||||
end
|
||||
|
||||
def set_account_moderation_note
|
||||
@account_moderation_note = AccountModerationNote.find(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account_moderation_note = AccountModerationNote.find(params[:id])
|
||||
@target_account = @account_moderation_note.target_account
|
||||
@account_moderation_note.destroy
|
||||
redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:account_moderation_note).permit(
|
||||
:content,
|
||||
:target_account_id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@@ -2,29 +2,57 @@
|
||||
|
||||
module Admin
|
||||
class AccountsController < BaseController
|
||||
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload]
|
||||
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :enable, :disable, :memorialize]
|
||||
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
|
||||
before_action :require_local_account!, only: [:enable, :disable, :memorialize]
|
||||
|
||||
def index
|
||||
authorize :account, :index?
|
||||
@accounts = filtered_accounts.page(params[:page])
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @account, :show?
|
||||
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
|
||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||
end
|
||||
|
||||
def subscribe
|
||||
authorize @account, :subscribe?
|
||||
Pubsubhubbub::SubscribeWorker.perform_async(@account.id)
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def unsubscribe
|
||||
authorize @account, :unsubscribe?
|
||||
Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id)
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def memorialize
|
||||
authorize @account, :memorialize?
|
||||
@account.memorialize!
|
||||
log_action :memorialize, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def enable
|
||||
authorize @account.user, :enable?
|
||||
@account.user.enable!
|
||||
log_action :enable, @account.user
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def disable
|
||||
authorize @account.user, :disable?
|
||||
@account.user.disable!
|
||||
log_action :disable, @account.user
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def redownload
|
||||
authorize @account, :redownload?
|
||||
|
||||
@account.reset_avatar!
|
||||
@account.reset_header!
|
||||
@account.save!
|
||||
@@ -32,6 +60,17 @@ module Admin
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def remove_avatar
|
||||
authorize @account, :remove_avatar?
|
||||
|
||||
@account.avatar = nil
|
||||
@account.save!
|
||||
|
||||
log_action :remove_avatar, @account.user
|
||||
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@@ -42,6 +81,10 @@ module Admin
|
||||
redirect_to admin_account_path(@account.id) if @account.local?
|
||||
end
|
||||
|
||||
def require_local_account!
|
||||
redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
|
||||
end
|
||||
|
||||
def filtered_accounts
|
||||
AccountFilter.new(filter_params).results
|
||||
end
|
||||
@@ -52,12 +95,13 @@ module Admin
|
||||
:remote,
|
||||
:by_domain,
|
||||
:silenced,
|
||||
:recent,
|
||||
:alphabetic,
|
||||
:suspended,
|
||||
:username,
|
||||
:display_name,
|
||||
:email,
|
||||
:ip
|
||||
:ip,
|
||||
:staff
|
||||
)
|
||||
end
|
||||
end
|
||||
|
9
app/controllers/admin/action_logs_controller.rb
Normal file
9
app/controllers/admin/action_logs_controller.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ActionLogsController < BaseController
|
||||
def index
|
||||
@action_logs = Admin::ActionLog.page(params[:page])
|
||||
end
|
||||
end
|
||||
end
|
@@ -2,7 +2,10 @@
|
||||
|
||||
module Admin
|
||||
class BaseController < ApplicationController
|
||||
before_action :require_admin!
|
||||
include Authorization
|
||||
include AccountableConcern
|
||||
|
||||
before_action :require_staff!
|
||||
|
||||
layout 'admin'
|
||||
end
|
||||
|
49
app/controllers/admin/change_emails_controller.rb
Normal file
49
app/controllers/admin/change_emails_controller.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ChangeEmailsController < BaseController
|
||||
before_action :set_account
|
||||
before_action :require_local_account!
|
||||
|
||||
def show
|
||||
authorize @user, :change_email?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @user, :change_email?
|
||||
|
||||
new_email = resource_params.fetch(:unconfirmed_email)
|
||||
|
||||
if new_email != @user.email
|
||||
@user.update!(
|
||||
unconfirmed_email: new_email,
|
||||
# Regenerate the confirmation token:
|
||||
confirmation_token: nil
|
||||
)
|
||||
|
||||
log_action :change_email, @user
|
||||
|
||||
@user.send_confirmation_instructions
|
||||
end
|
||||
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.change_email.changed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
@user = @account.user
|
||||
end
|
||||
|
||||
def require_local_account!
|
||||
redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user).permit(
|
||||
:unconfirmed_email
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
@@ -2,15 +2,38 @@
|
||||
|
||||
module Admin
|
||||
class ConfirmationsController < BaseController
|
||||
before_action :set_user
|
||||
before_action :check_confirmation, only: [:resend]
|
||||
|
||||
def create
|
||||
account_user.confirm
|
||||
authorize @user, :confirm?
|
||||
@user.confirm!
|
||||
log_action :confirm, @user
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def resend
|
||||
authorize @user, :confirm?
|
||||
|
||||
@user.resend_confirmation_instructions
|
||||
|
||||
log_action :confirm, @user
|
||||
|
||||
flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_user
|
||||
Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||
def set_user
|
||||
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
def check_confirmation
|
||||
if @user.confirmed?
|
||||
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -3,48 +3,82 @@
|
||||
module Admin
|
||||
class CustomEmojisController < BaseController
|
||||
before_action :set_custom_emoji, except: [:index, :new, :create]
|
||||
before_action :set_filter_params
|
||||
|
||||
def index
|
||||
@custom_emojis = filtered_custom_emojis.page(params[:page])
|
||||
authorize :custom_emoji, :index?
|
||||
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :custom_emoji, :create?
|
||||
@custom_emoji = CustomEmoji.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :custom_emoji, :create?
|
||||
|
||||
@custom_emoji = CustomEmoji.new(resource_params)
|
||||
|
||||
if @custom_emoji.save
|
||||
log_action :create, @custom_emoji
|
||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.created_msg')
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @custom_emoji, :update?
|
||||
|
||||
if @custom_emoji.update(resource_params)
|
||||
log_action :update, @custom_emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.updated_msg')
|
||||
else
|
||||
flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg')
|
||||
end
|
||||
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@custom_emoji.destroy
|
||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg')
|
||||
authorize @custom_emoji, :destroy?
|
||||
@custom_emoji.destroy!
|
||||
log_action :destroy, @custom_emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg')
|
||||
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||
end
|
||||
|
||||
def copy
|
||||
emoji = CustomEmoji.new(domain: nil, shortcode: @custom_emoji.shortcode, image: @custom_emoji.image)
|
||||
authorize @custom_emoji, :copy?
|
||||
|
||||
emoji = CustomEmoji.find_or_initialize_by(domain: nil,
|
||||
shortcode: @custom_emoji.shortcode)
|
||||
emoji.image = @custom_emoji.image
|
||||
|
||||
if emoji.save
|
||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.copied_msg')
|
||||
log_action :create, emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
|
||||
else
|
||||
redirect_to admin_custom_emojis_path, alert: I18n.t('admin.custom_emojis.copy_failed_msg')
|
||||
flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
|
||||
end
|
||||
|
||||
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||
end
|
||||
|
||||
def enable
|
||||
authorize @custom_emoji, :enable?
|
||||
@custom_emoji.update!(disabled: false)
|
||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg')
|
||||
log_action :enable, @custom_emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg')
|
||||
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||
end
|
||||
|
||||
def disable
|
||||
authorize @custom_emoji, :disable?
|
||||
@custom_emoji.update!(disabled: true)
|
||||
redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg')
|
||||
log_action :disable, @custom_emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg')
|
||||
redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -53,8 +87,12 @@ module Admin
|
||||
@custom_emoji = CustomEmoji.find(params[:id])
|
||||
end
|
||||
|
||||
def set_filter_params
|
||||
@filter_params = filter_params.to_hash.symbolize_keys
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:custom_emoji).permit(:shortcode, :image)
|
||||
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
||||
end
|
||||
|
||||
def filtered_custom_emojis
|
||||
@@ -64,7 +102,9 @@ module Admin
|
||||
def filter_params
|
||||
params.permit(
|
||||
:local,
|
||||
:remote
|
||||
:remote,
|
||||
:by_domain,
|
||||
:shortcode
|
||||
)
|
||||
end
|
||||
end
|
||||
|
43
app/controllers/admin/dashboard_controller.rb
Normal file
43
app/controllers/admin/dashboard_controller.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
require 'sidekiq/api'
|
||||
|
||||
module Admin
|
||||
class DashboardController < BaseController
|
||||
def index
|
||||
@users_count = User.count
|
||||
@registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0
|
||||
@logins_week = Redis.current.pfcount("activity:logins:#{current_week}")
|
||||
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
|
||||
@relay_enabled = Relay.enabled.exists?
|
||||
@single_user_mode = Rails.configuration.x.single_user_mode
|
||||
@registrations_enabled = Setting.open_registrations
|
||||
@deletions_enabled = Setting.open_deletion
|
||||
@invites_enabled = Setting.min_invite_role == 'user'
|
||||
@search_enabled = Chewy.enabled?
|
||||
@version = Mastodon::Version.to_s
|
||||
@database_version = ActiveRecord::Base.connection.execute('SELECT VERSION()').first['version'].match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
|
||||
@redis_version = redis_info['redis_version']
|
||||
@reports_count = Report.unresolved.count
|
||||
@queue_backlog = Sidekiq::Stats.new.enqueued
|
||||
@recent_users = User.confirmed.recent.includes(:account).limit(4)
|
||||
@database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
||||
@redis_size = redis_info['used_memory']
|
||||
@ldap_enabled = ENV['LDAP_ENABLED'] == 'true'
|
||||
@cas_enabled = ENV['CAS_ENABLED'] == 'true'
|
||||
@saml_enabled = ENV['SAML_ENABLED'] == 'true'
|
||||
@pam_enabled = ENV['PAM_ENABLED'] == 'true'
|
||||
@hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
|
||||
@trending_hashtags = TrendingTags.get(7)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_week
|
||||
@current_week ||= Time.now.utc.to_date.cweek
|
||||
end
|
||||
|
||||
def redis_info
|
||||
@redis_info ||= Redis.current.info
|
||||
end
|
||||
end
|
||||
end
|
@@ -5,28 +5,37 @@ module Admin
|
||||
before_action :set_domain_block, only: [:show, :destroy]
|
||||
|
||||
def index
|
||||
authorize :domain_block, :index?
|
||||
@domain_blocks = DomainBlock.page(params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :domain_block, :create?
|
||||
@domain_block = DomainBlock.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :domain_block, :create?
|
||||
|
||||
@domain_block = DomainBlock.new(resource_params)
|
||||
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id)
|
||||
log_action :create, @domain_block
|
||||
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.created_msg')
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def show; end
|
||||
def show
|
||||
authorize @domain_block, :show?
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @domain_block, :destroy?
|
||||
UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
|
||||
log_action :destroy, @domain_block
|
||||
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
||||
end
|
||||
|
||||
@@ -37,7 +46,7 @@ module Admin
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
|
||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
|
||||
end
|
||||
|
||||
def retroactive_unblock?
|
||||
|
@@ -5,17 +5,22 @@ module Admin
|
||||
before_action :set_email_domain_block, only: [:show, :destroy]
|
||||
|
||||
def index
|
||||
authorize :email_domain_block, :index?
|
||||
@email_domain_blocks = EmailDomainBlock.page(params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :email_domain_block, :create?
|
||||
@email_domain_block = EmailDomainBlock.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :email_domain_block, :create?
|
||||
|
||||
@email_domain_block = EmailDomainBlock.new(resource_params)
|
||||
|
||||
if @email_domain_block.save
|
||||
log_action :create, @email_domain_block
|
||||
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.created_msg')
|
||||
else
|
||||
render :new
|
||||
@@ -23,7 +28,9 @@ module Admin
|
||||
end
|
||||
|
||||
def destroy
|
||||
@email_domain_block.destroy
|
||||
authorize @email_domain_block, :destroy?
|
||||
@email_domain_block.destroy!
|
||||
log_action :destroy, @email_domain_block
|
||||
redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg')
|
||||
end
|
||||
|
||||
|
@@ -3,10 +3,12 @@
|
||||
module Admin
|
||||
class InstancesController < BaseController
|
||||
def index
|
||||
authorize :instance, :index?
|
||||
@instances = ordered_instances
|
||||
end
|
||||
|
||||
def resubscribe
|
||||
authorize :instance, :resubscribe?
|
||||
params.require(:by_domain)
|
||||
Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id))
|
||||
redirect_to admin_instances_path
|
||||
|
53
app/controllers/admin/invites_controller.rb
Normal file
53
app/controllers/admin/invites_controller.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class InvitesController < BaseController
|
||||
def index
|
||||
authorize :invite, :index?
|
||||
|
||||
@invites = filtered_invites.includes(user: :account).page(params[:page])
|
||||
@invite = Invite.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :invite, :create?
|
||||
|
||||
@invite = Invite.new(resource_params)
|
||||
@invite.user = current_user
|
||||
|
||||
if @invite.save
|
||||
redirect_to admin_invites_path
|
||||
else
|
||||
@invites = Invite.page(params[:page])
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@invite = Invite.find(params[:id])
|
||||
authorize @invite, :destroy?
|
||||
@invite.expire!
|
||||
redirect_to admin_invites_path
|
||||
end
|
||||
|
||||
def deactivate_all
|
||||
authorize :invite, :deactivate_all?
|
||||
Invite.available.in_batches.update_all(expires_at: Time.now.utc)
|
||||
redirect_to admin_invites_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:invite).permit(:max_uses, :expires_in)
|
||||
end
|
||||
|
||||
def filtered_invites
|
||||
InviteFilter.new(filter_params).results
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.permit(:available, :expired)
|
||||
end
|
||||
end
|
||||
end
|
58
app/controllers/admin/relays_controller.rb
Normal file
58
app/controllers/admin/relays_controller.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class RelaysController < BaseController
|
||||
before_action :set_relay, except: [:index, :new, :create]
|
||||
|
||||
def index
|
||||
authorize :relay, :update?
|
||||
@relays = Relay.all
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :relay, :update?
|
||||
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY)
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :relay, :update?
|
||||
|
||||
@relay = Relay.new(resource_params)
|
||||
|
||||
if @relay.save
|
||||
@relay.enable!
|
||||
redirect_to admin_relays_path
|
||||
else
|
||||
render action: :new
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize :relay, :update?
|
||||
@relay.destroy
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
def enable
|
||||
authorize :relay, :update?
|
||||
@relay.enable!
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
def disable
|
||||
authorize :relay, :update?
|
||||
@relay.disable!
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_relay
|
||||
@relay = Relay.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:relay).permit(:inbox_url)
|
||||
end
|
||||
end
|
||||
end
|
56
app/controllers/admin/report_notes_controller.rb
Normal file
56
app/controllers/admin/report_notes_controller.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ReportNotesController < BaseController
|
||||
before_action :set_report_note, only: [:destroy]
|
||||
|
||||
def create
|
||||
authorize ReportNote, :create?
|
||||
|
||||
@report_note = current_account.report_notes.new(resource_params)
|
||||
@report = @report_note.report
|
||||
|
||||
if @report_note.save
|
||||
if params[:create_and_resolve]
|
||||
@report.resolve!(current_account)
|
||||
log_action :resolve, @report
|
||||
|
||||
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
|
||||
return
|
||||
end
|
||||
|
||||
if params[:create_and_unresolve]
|
||||
@report.unresolve!
|
||||
log_action :reopen, @report
|
||||
end
|
||||
|
||||
redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg')
|
||||
else
|
||||
@report_notes = @report.notes.latest
|
||||
@report_history = @report.history
|
||||
@form = Form::StatusBatch.new
|
||||
|
||||
render template: 'admin/reports/show'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @report_note, :destroy?
|
||||
@report_note.destroy!
|
||||
redirect_to admin_report_path(@report_note.report_id), notice: I18n.t('admin.report_notes.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:report_note).permit(
|
||||
:content,
|
||||
:report_id
|
||||
)
|
||||
end
|
||||
|
||||
def set_report_note
|
||||
@report_note = ReportNote.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
@@ -2,29 +2,17 @@
|
||||
|
||||
module Admin
|
||||
class ReportedStatusesController < BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :set_report
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
def create
|
||||
@form = Form::StatusBatch.new(form_status_batch_params)
|
||||
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
|
||||
authorize :status, :update?
|
||||
|
||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
||||
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def update
|
||||
@status.update(status_params)
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
render json: @status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_params
|
||||
@@ -32,15 +20,21 @@ module Admin
|
||||
end
|
||||
|
||||
def form_status_batch_params
|
||||
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||
params.require(:form_status_batch).permit(status_ids: [])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
if params[:nsfw_on]
|
||||
'nsfw_on'
|
||||
elsif params[:nsfw_off]
|
||||
'nsfw_off'
|
||||
elsif params[:delete]
|
||||
'delete'
|
||||
end
|
||||
end
|
||||
|
||||
def set_report
|
||||
@report = Report.find(params[:report_id])
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = @report.statuses.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -5,43 +5,70 @@ module Admin
|
||||
before_action :set_report, except: [:index]
|
||||
|
||||
def index
|
||||
authorize :report, :index?
|
||||
@reports = filtered_reports.page(params[:page])
|
||||
end
|
||||
|
||||
def show
|
||||
@form = Form::StatusBatch.new
|
||||
authorize @report, :show?
|
||||
|
||||
@report_note = @report.notes.new
|
||||
@report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at)
|
||||
@form = Form::StatusBatch.new
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @report, :update?
|
||||
process_report
|
||||
redirect_to admin_report_path(@report)
|
||||
|
||||
if @report.action_taken?
|
||||
redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg')
|
||||
else
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_report
|
||||
case params[:outcome].to_s
|
||||
when 'assign_to_self'
|
||||
@report.update!(assigned_account_id: current_account.id)
|
||||
log_action :assigned_to_self, @report
|
||||
when 'unassign'
|
||||
@report.update!(assigned_account_id: nil)
|
||||
log_action :unassigned, @report
|
||||
when 'reopen'
|
||||
@report.unresolve!
|
||||
log_action :reopen, @report
|
||||
when 'resolve'
|
||||
@report.update(action_taken_by_current_attributes)
|
||||
when 'suspend'
|
||||
Admin::SuspensionWorker.perform_async(@report.target_account.id)
|
||||
@report.resolve!(current_account)
|
||||
log_action :resolve, @report
|
||||
when 'disable'
|
||||
@report.resolve!(current_account)
|
||||
@report.target_account.user.disable!
|
||||
|
||||
log_action :resolve, @report
|
||||
log_action :disable, @report.target_account.user
|
||||
|
||||
resolve_all_target_account_reports
|
||||
when 'silence'
|
||||
@report.target_account.update(silenced: true)
|
||||
@report.resolve!(current_account)
|
||||
@report.target_account.update!(silenced: true)
|
||||
|
||||
log_action :resolve, @report
|
||||
log_action :silence, @report.target_account
|
||||
|
||||
resolve_all_target_account_reports
|
||||
else
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
|
||||
def action_taken_by_current_attributes
|
||||
{ action_taken: true, action_taken_by_account_id: current_account.id }
|
||||
@report.reload
|
||||
end
|
||||
|
||||
def resolve_all_target_account_reports
|
||||
unresolved_reports_for_target_account.update_all(
|
||||
action_taken_by_current_attributes
|
||||
)
|
||||
unresolved_reports_for_target_account.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
end
|
||||
|
||||
def unresolved_reports_for_target_account
|
||||
|
@@ -2,17 +2,19 @@
|
||||
|
||||
module Admin
|
||||
class ResetsController < BaseController
|
||||
before_action :set_account
|
||||
before_action :set_user
|
||||
|
||||
def create
|
||||
@account.user.send_reset_password_instructions
|
||||
authorize @user, :reset_password?
|
||||
@user.send_reset_password_instructions
|
||||
log_action :reset_password, @user
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
def set_user
|
||||
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
27
app/controllers/admin/roles_controller.rb
Normal file
27
app/controllers/admin/roles_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class RolesController < BaseController
|
||||
before_action :set_user
|
||||
|
||||
def promote
|
||||
authorize @user, :promote?
|
||||
@user.promote!
|
||||
log_action :promote, @user
|
||||
redirect_to admin_account_path(@user.account_id)
|
||||
end
|
||||
|
||||
def demote
|
||||
authorize @user, :demote?
|
||||
@user.demote!
|
||||
log_action :demote, @user
|
||||
redirect_to admin_account_path(@user.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
@user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
@@ -6,6 +6,7 @@ module Admin
|
||||
site_contact_username
|
||||
site_contact_email
|
||||
site_title
|
||||
site_short_description
|
||||
site_description
|
||||
site_extended_description
|
||||
site_terms
|
||||
@@ -13,25 +14,45 @@ module Admin
|
||||
closed_registrations_message
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
bootstrap_timeline_accounts
|
||||
theme
|
||||
thumbnail
|
||||
hero
|
||||
mascot
|
||||
min_invite_role
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
preview_sensitive_media
|
||||
custom_css
|
||||
).freeze
|
||||
|
||||
BOOLEAN_SETTINGS = %w(
|
||||
open_registrations
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
preview_sensitive_media
|
||||
).freeze
|
||||
|
||||
UPLOAD_SETTINGS = %w(
|
||||
thumbnail
|
||||
hero
|
||||
mascot
|
||||
).freeze
|
||||
|
||||
def edit
|
||||
authorize :settings, :show?
|
||||
@admin_settings = Form::AdminSettings.new
|
||||
end
|
||||
|
||||
def update
|
||||
authorize :settings, :update?
|
||||
|
||||
settings_params.each do |key, value|
|
||||
if UPLOAD_SETTINGS.include?(key)
|
||||
upload = SiteUpload.where(var: key).first_or_initialize(var: key)
|
||||
|
@@ -5,12 +5,16 @@ module Admin
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
@account.update(silenced: true)
|
||||
authorize @account, :silence?
|
||||
@account.update!(silenced: true)
|
||||
log_action :silence, @account
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account.update(silenced: false)
|
||||
authorize @account, :unsilence?
|
||||
@account.update!(silenced: false)
|
||||
log_action :unsilence, @account
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
|
@@ -2,68 +2,66 @@
|
||||
|
||||
module Admin
|
||||
class StatusesController < BaseController
|
||||
include Authorization
|
||||
|
||||
helper_method :current_params
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
PER_PAGE = 20
|
||||
|
||||
def index
|
||||
@statuses = @account.statuses
|
||||
authorize :status, :index?
|
||||
|
||||
@statuses = @account.statuses.where(visibility: [:public, :unlisted])
|
||||
|
||||
if params[:media]
|
||||
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
@statuses.merge!(Status.where(id: account_media_status_ids))
|
||||
end
|
||||
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
|
||||
|
||||
@form = Form::StatusBatch.new
|
||||
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
|
||||
@form = Form::StatusBatch.new
|
||||
end
|
||||
|
||||
def create
|
||||
@form = Form::StatusBatch.new(form_status_batch_params)
|
||||
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
|
||||
authorize :status, :update?
|
||||
|
||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
||||
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
|
||||
|
||||
def update
|
||||
@status.update(status_params)
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
render json: @status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_params
|
||||
params.require(:status).permit(:sensitive)
|
||||
end
|
||||
|
||||
def form_status_batch_params
|
||||
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = @account.statuses.find(params[:id])
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def current_params
|
||||
page = (params[:page] || 1).to_i
|
||||
|
||||
{
|
||||
media: params[:media],
|
||||
page: page > 1 && page,
|
||||
}.select { |_, value| value.present? }
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
if params[:nsfw_on]
|
||||
'nsfw_on'
|
||||
elsif params[:nsfw_off]
|
||||
'nsfw_off'
|
||||
elsif params[:delete]
|
||||
'delete'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -3,6 +3,7 @@
|
||||
module Admin
|
||||
class SubscriptionsController < BaseController
|
||||
def index
|
||||
authorize :subscription, :index?
|
||||
@subscriptions = ordered_subscriptions.page(requested_page)
|
||||
end
|
||||
|
||||
|
@@ -4,13 +4,30 @@ module Admin
|
||||
class SuspensionsController < BaseController
|
||||
before_action :set_account
|
||||
|
||||
def new
|
||||
@suspension = Form::AdminSuspensionConfirmation.new(report_id: params[:report_id])
|
||||
end
|
||||
|
||||
def create
|
||||
Admin::SuspensionWorker.perform_async(@account.id)
|
||||
redirect_to admin_accounts_path
|
||||
authorize @account, :suspend?
|
||||
|
||||
@suspension = Form::AdminSuspensionConfirmation.new(suspension_params)
|
||||
|
||||
if suspension_params[:acct] == @account.acct
|
||||
resolve_report! if suspension_params[:report_id].present?
|
||||
perform_suspend!
|
||||
mark_reports_resolved!
|
||||
redirect_to admin_accounts_path
|
||||
else
|
||||
flash.now[:alert] = I18n.t('admin.suspensions.bad_acct_msg')
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account.update(suspended: false)
|
||||
authorize @account, :unsuspend?
|
||||
@account.unsuspend!
|
||||
log_action :unsuspend, @account
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
@@ -19,5 +36,25 @@ module Admin
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def suspension_params
|
||||
params.require(:form_admin_suspension_confirmation).permit(:acct, :report_id)
|
||||
end
|
||||
|
||||
def resolve_report!
|
||||
report = Report.find(suspension_params[:report_id])
|
||||
report.resolve!(current_account)
|
||||
log_action :resolve, report
|
||||
end
|
||||
|
||||
def perform_suspend!
|
||||
@account.suspend!
|
||||
Admin::SuspensionWorker.perform_async(@account.id)
|
||||
log_action :suspend, @account
|
||||
end
|
||||
|
||||
def mark_reports_resolved!
|
||||
Report.where(target_account: @account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -5,7 +5,9 @@ module Admin
|
||||
before_action :set_user
|
||||
|
||||
def destroy
|
||||
authorize @user, :disable_2fa?
|
||||
@user.disable_two_factor!
|
||||
log_action :disable_2fa, @user
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
|
@@ -6,8 +6,10 @@ class Api::BaseController < ApplicationController
|
||||
|
||||
include RateLimitHeaders
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_before_action :store_current_location
|
||||
skip_before_action :check_user_permissions
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
@@ -51,6 +53,10 @@ class Api::BaseController < ApplicationController
|
||||
[params[:limit].to_i.abs, default_limit * 2].min
|
||||
end
|
||||
|
||||
def params_slice(*keys)
|
||||
params.slice(*keys).permit(*keys)
|
||||
end
|
||||
|
||||
def current_resource_owner
|
||||
@current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
||||
end
|
||||
@@ -62,8 +68,10 @@ class Api::BaseController < ApplicationController
|
||||
end
|
||||
|
||||
def require_user!
|
||||
if current_user
|
||||
if current_user && !current_user.disabled?
|
||||
set_user_activity
|
||||
elsif current_user
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
else
|
||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||
end
|
||||
@@ -73,18 +81,7 @@ class Api::BaseController < ApplicationController
|
||||
render json: {}, status: 200
|
||||
end
|
||||
|
||||
def set_maps(statuses) # rubocop:disable Style/AccessorMethodName
|
||||
if current_account.nil?
|
||||
@reblogs_map = {}
|
||||
@favourites_map = {}
|
||||
@mutes_map = {}
|
||||
return
|
||||
end
|
||||
|
||||
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
||||
conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
|
||||
@reblogs_map = Status.reblogs_map(status_ids, current_account)
|
||||
@favourites_map = Status.favourites_map(status_ids, current_account)
|
||||
@mutes_map = Status.mutes_map(conversation_ids, current_account)
|
||||
def authorize_if_got_token!(*scopes)
|
||||
doorkeeper_authorize!(*scopes) if doorkeeper_token
|
||||
end
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::SalmonController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
respond_to :txt
|
||||
|
||||
@@ -9,7 +11,7 @@ class Api::SalmonController < Api::BaseController
|
||||
process_salmon
|
||||
head 202
|
||||
elsif payload.present?
|
||||
[signature_verification_failure_reason, 401]
|
||||
render plain: signature_verification_failure_reason, status: 401
|
||||
else
|
||||
head 400
|
||||
end
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, except: [:update]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:update]
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:update]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||
before_action :require_user!
|
||||
|
||||
def show
|
||||
@@ -13,6 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
def update
|
||||
@account = current_account
|
||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||
UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
|
||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
@@ -20,6 +21,18 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.permit(:display_name, :note, :avatar, :header)
|
||||
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
|
||||
end
|
||||
|
||||
def user_settings_params
|
||||
return nil unless params.key?(:source)
|
||||
|
||||
source_params = params.require(:source)
|
||||
|
||||
{
|
||||
'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy),
|
||||
'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
|
||||
'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -19,6 +19,8 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
return [] if @account.user_hides_network? && current_account.id != @account.id
|
||||
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
@@ -63,6 +65,6 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -19,6 +19,8 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
return [] if @account.user_hides_network? && current_account.id != @account.id
|
||||
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
@@ -63,6 +65,6 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
20
app/controllers/api/v1/accounts/lists_controller.rb
Normal file
20
app/controllers/api/v1/accounts/lists_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::ListsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
|
||||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@lists = @account.lists.where(account: current_account)
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
end
|
32
app/controllers/api/v1/accounts/pins_controller.rb
Normal file
32
app/controllers/api/v1/accounts/pins_controller.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||
before_action :require_user!
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
AccountPin.create!(account: current_account, target_account: @account)
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
||||
end
|
||||
|
||||
def destroy
|
||||
pin = AccountPin.find_by(account: current_account, target_account: @account)
|
||||
pin&.destroy!
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def relationships_presenter
|
||||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id)
|
||||
end
|
||||
end
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:follows' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
@@ -10,7 +10,7 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||
accounts = Account.where(id: account_ids).select('id')
|
||||
# .where doesn't guarantee that our results are in the same order
|
||||
# we requested them, so return the "right" order to the requestor.
|
||||
@accounts = accounts.index_by(&:id).values_at(*account_ids)
|
||||
@accounts = accounts.index_by(&:id).values_at(*account_ids).compact
|
||||
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
@@ -21,6 +21,6 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||
end
|
||||
|
||||
def account_ids
|
||||
@_account_ids ||= Array(params[:id]).map(&:to_i)
|
||||
Array(params[:id]).map(&:to_i)
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::SearchController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
@@ -17,12 +17,9 @@ class Api::V1::Accounts::SearchController < Api::BaseController
|
||||
AccountSearchService.new.call(
|
||||
params[:q],
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
resolving_search?,
|
||||
current_account
|
||||
current_account,
|
||||
resolve: truthy_param?(:resolve),
|
||||
following: truthy_param?(:following)
|
||||
)
|
||||
end
|
||||
|
||||
def resolving_search?
|
||||
params[:resolve] == 'true'
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -27,19 +27,16 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
end
|
||||
|
||||
def account_statuses
|
||||
default_statuses.tap do |statuses|
|
||||
statuses.merge!(only_media_scope) if params[:only_media]
|
||||
statuses.merge!(pinned_scope) if params[:pinned]
|
||||
statuses.merge!(no_replies_scope) if params[:exclude_replies]
|
||||
end
|
||||
end
|
||||
|
||||
def default_statuses
|
||||
permitted_account_statuses.paginate_by_max_id(
|
||||
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
|
||||
statuses = statuses.paginate_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
|
||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
||||
|
||||
statuses
|
||||
end
|
||||
|
||||
def permitted_account_statuses
|
||||
@@ -51,7 +48,13 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
end
|
||||
|
||||
def account_media_status_ids
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
|
||||
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
||||
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
||||
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
.reorder(id: :desc).distinct(:id).pluck(:id)
|
||||
end
|
||||
|
||||
def pinned_scope
|
||||
@@ -63,7 +66,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
@@ -78,7 +81,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
|
||||
def prev_path
|
||||
unless @statuses.empty?
|
||||
api_v1_account_statuses_url pagination_params(since_id: pagination_since_id)
|
||||
api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -1,10 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
|
||||
|
||||
before_action :require_user!, except: [:show]
|
||||
before_action :set_account
|
||||
before_action :check_account_suspension, only: [:show]
|
||||
|
||||
respond_to :json
|
||||
|
||||
@@ -13,9 +17,9 @@ class Api::V1::AccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account.acct)
|
||||
FollowService.new.call(current_user.account, @account.acct, reblogs: truthy_param?(:reblogs))
|
||||
|
||||
options = @account.locked? ? {} : { following_map: { @account.id => true }, requested_map: { @account.id => false } }
|
||||
options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
|
||||
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
|
||||
end
|
||||
@@ -26,7 +30,7 @@ class Api::V1::AccountsController < Api::BaseController
|
||||
end
|
||||
|
||||
def mute
|
||||
MuteService.new.call(current_user.account, @account)
|
||||
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
@@ -51,7 +55,11 @@ class Api::V1::AccountsController < Api::BaseController
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def relationships(options = {})
|
||||
def relationships(**options)
|
||||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::BlocksController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -57,6 +57,6 @@ class Api::V1::BlocksController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
71
app/controllers/api/v1/conversations_controller.rb
Normal file
71
app/controllers/api/v1/conversations_controller.rb
Normal file
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ConversationsController < Api::BaseController
|
||||
LIMIT = 20
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
|
||||
before_action :require_user!
|
||||
before_action :set_conversation, except: :index
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@conversations = paginated_conversations
|
||||
render json: @conversations, each_serializer: REST::ConversationSerializer
|
||||
end
|
||||
|
||||
def read
|
||||
@conversation.update!(unread: false)
|
||||
render json: @conversation, serializer: REST::ConversationSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@conversation.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_conversation
|
||||
@conversation = AccountConversation.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def paginated_conversations
|
||||
AccountConversation.where(account: current_account)
|
||||
.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_conversations_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @conversations.empty?
|
||||
api_v1_conversations_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@conversations.last.last_status_id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@conversations.first.last_status_id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@conversations.size == limit_param(LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
@@ -3,7 +3,8 @@
|
||||
class Api::V1::DomainBlocksController < Api::BaseController
|
||||
BLOCK_LIMIT = 100
|
||||
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
@@ -15,7 +16,8 @@ class Api::V1::DomainBlocksController < Api::BaseController
|
||||
end
|
||||
|
||||
def create
|
||||
BlockDomainFromAccountService.new.call(current_account, domain_block_params[:domain])
|
||||
current_account.block_domain!(domain_block_params[:domain])
|
||||
AfterAccountDomainBlockWorker.perform_async(current_account.id, domain_block_params[:domain])
|
||||
render_empty
|
||||
end
|
||||
|
||||
@@ -67,7 +69,7 @@ class Api::V1::DomainBlocksController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def domain_block_params
|
||||
|
72
app/controllers/api/v1/endorsements_controller.rb
Normal file
72
app/controllers/api/v1/endorsements_controller.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::EndorsementsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
endorsed_accounts.all
|
||||
else
|
||||
endorsed_accounts.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def endorsed_accounts
|
||||
current_account.endorsed_accounts
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
return if unlimited?
|
||||
|
||||
if records_continue?
|
||||
api_v1_endorsements_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if unlimited?
|
||||
|
||||
unless @accounts.empty?
|
||||
api_v1_endorsements_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def unlimited?
|
||||
params[:limit] == '0'
|
||||
end
|
||||
end
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FavouritesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:favourites' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -26,10 +26,9 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||
end
|
||||
|
||||
def results
|
||||
@_results ||= account_favourites.paginate_by_max_id(
|
||||
@_results ||= account_favourites.paginate_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -49,7 +48,7 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||
|
||||
def prev_path
|
||||
unless results.empty?
|
||||
api_v1_favourites_url pagination_params(since_id: pagination_since_id)
|
||||
api_v1_favourites_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,6 +65,6 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
48
app/controllers/api/v1/filters_controller.rb
Normal file
48
app/controllers/api/v1/filters_controller.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FiltersController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
|
||||
before_action :require_user!
|
||||
before_action :set_filters, only: :index
|
||||
before_action :set_filter, only: [:show, :update, :destroy]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @filters, each_serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@filter = current_account.custom_filters.create!(resource_params)
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@filter.update!(resource_params)
|
||||
render json: @filter, serializer: REST::FilterSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@filter.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_filters
|
||||
@filters = current_account.custom_filters
|
||||
end
|
||||
|
||||
def set_filter
|
||||
@filter = current_account.custom_filters.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:phrase, :expires_in, :irreversible, :whole_word, context: [])
|
||||
end
|
||||
end
|
@@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowRequestsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
@@ -12,6 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||
|
||||
def authorize
|
||||
AuthorizeFollowService.new.call(account, current_account)
|
||||
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
|
||||
render_empty
|
||||
end
|
||||
|
||||
@@ -71,6 +73,6 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
36
app/controllers/api/v1/instances/activity_controller.rb
Normal file
36
app/controllers/api/v1/instances/activity_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Instances::ActivityController < Api::BaseController
|
||||
before_action :require_enabled_api!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def activity
|
||||
weeks = []
|
||||
|
||||
12.times do |i|
|
||||
day = i.weeks.ago.to_date
|
||||
week_id = day.cweek
|
||||
week = Date.commercial(day.cwyear, week_id)
|
||||
|
||||
weeks << {
|
||||
week: week.to_time.to_i.to_s,
|
||||
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
|
||||
logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
|
||||
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
|
||||
}
|
||||
end
|
||||
|
||||
weeks
|
||||
end
|
||||
|
||||
def require_enabled_api!
|
||||
head 404 unless Setting.activity_api_enabled
|
||||
end
|
||||
end
|
17
app/controllers/api/v1/instances/peers_controller.rb
Normal file
17
app/controllers/api/v1/instances/peers_controller.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Instances::PeersController < Api::BaseController
|
||||
before_action :require_enabled_api!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_enabled_api!
|
||||
head 404 unless Setting.peers_api_enabled
|
||||
end
|
||||
end
|
@@ -4,6 +4,8 @@ class Api::V1::InstancesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: {}, serializer: REST::InstanceSerializer
|
||||
render_cached_json('api:v1:instances', expires_in: 5.minutes) do
|
||||
ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
97
app/controllers/api/v1/lists/accounts_controller.rb
Normal file
97
app/controllers/api/v1/lists/accounts_controller.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Lists::AccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_list
|
||||
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
def show
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
ApplicationRecord.transaction do
|
||||
list_accounts.each do |account|
|
||||
@list.accounts << account
|
||||
end
|
||||
end
|
||||
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy
|
||||
ListAccount.where(list: @list, account_id: account_ids).destroy_all
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:list_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@list.accounts.all
|
||||
else
|
||||
@list.accounts.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
def list_accounts
|
||||
Account.find(account_ids)
|
||||
end
|
||||
|
||||
def account_ids
|
||||
Array(resource_params[:account_ids])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(account_ids: [])
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
return if unlimited?
|
||||
|
||||
if records_continue?
|
||||
api_v1_list_accounts_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
return if unlimited?
|
||||
|
||||
unless @accounts.empty?
|
||||
api_v1_list_accounts_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def unlimited?
|
||||
params[:limit] == '0'
|
||||
end
|
||||
end
|
43
app/controllers/api/v1/lists_controller.rb
Normal file
43
app/controllers/api/v1/lists_controller.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ListsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_list, except: [:index, :create]
|
||||
|
||||
def index
|
||||
@lists = List.where(account: current_account).all
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@list = List.create!(list_params.merge(account: current_account))
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@list.update!(list_params)
|
||||
render json: @list, serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@list.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def list_params
|
||||
params.permit(:title)
|
||||
end
|
||||
end
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MediaController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:media' }
|
||||
before_action :require_user!
|
||||
|
||||
include ObfuscateFilename
|
||||
@@ -27,7 +27,7 @@ class Api::V1::MediaController < Api::BaseController
|
||||
private
|
||||
|
||||
def media_params
|
||||
params.permit(:file, :description)
|
||||
params.permit(:file, :description, :focus)
|
||||
end
|
||||
|
||||
def file_type_error
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MutesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action -> { doorkeeper_authorize! :follow, :'read:mutes' }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -15,19 +15,17 @@ class Api::V1::MutesController < Api::BaseController
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_mutes).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:muted_by).references(:muted_by)
|
||||
paginated_mutes.map(&:target_account)
|
||||
end
|
||||
|
||||
def paginated_mutes
|
||||
Mute.where(account: current_account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
@@ -41,24 +39,24 @@ class Api::V1::MutesController < Api::BaseController
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
unless paginated_mutes.empty?
|
||||
api_v1_mutes_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.muted_by_ids.last
|
||||
paginated_mutes.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.muted_by_ids.first
|
||||
paginated_mutes.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::NotificationsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
@@ -36,10 +37,9 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||
end
|
||||
|
||||
def paginated_notifications
|
||||
browserable_account_notifications.paginate_by_max_id(
|
||||
browserable_account_notifications.paginate_by_id(
|
||||
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||
|
||||
def prev_path
|
||||
unless @notifications.empty?
|
||||
api_v1_notifications_url pagination_params(since_id: pagination_since_id)
|
||||
api_v1_notifications_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -82,6 +82,6 @@ class Api::V1::NotificationsController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit, exclude_types: []).merge(core_params)
|
||||
params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
56
app/controllers/api/v1/push/subscriptions_controller.rb
Normal file
56
app/controllers/api/v1/push/subscriptions_controller.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Push::SubscriptionsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :push }
|
||||
before_action :require_user!
|
||||
before_action :set_web_push_subscription
|
||||
|
||||
def create
|
||||
@web_subscription&.destroy!
|
||||
|
||||
@web_subscription = ::Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data_params,
|
||||
user_id: current_user.id,
|
||||
access_token_id: doorkeeper_token.id
|
||||
)
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
|
||||
|
||||
@web_subscription.update!(data: data_params)
|
||||
|
||||
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@web_subscription&.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_web_push_subscription
|
||||
@web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
|
||||
end
|
||||
|
||||
def subscription_params
|
||||
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
|
||||
end
|
||||
|
||||
def data_params
|
||||
return {} if params[:data].blank?
|
||||
params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention])
|
||||
end
|
||||
end
|
@@ -1,33 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ReportsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, except: [:create]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@reports = current_account.reports
|
||||
render json: @reports, each_serializer: REST::ReportSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@report = current_account.reports.create!(
|
||||
target_account: reported_account,
|
||||
@report = ReportService.new.call(
|
||||
current_account,
|
||||
reported_account,
|
||||
status_ids: reported_status_ids,
|
||||
comment: report_params[:comment]
|
||||
comment: report_params[:comment],
|
||||
forward: report_params[:forward]
|
||||
)
|
||||
|
||||
User.admins.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later }
|
||||
|
||||
render json: @report, serializer: REST::ReportSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reported_status_ids
|
||||
Status.find(status_ids).pluck(:id)
|
||||
reported_account.statuses.find(status_ids).pluck(:id)
|
||||
end
|
||||
|
||||
def status_ids
|
||||
@@ -39,6 +33,6 @@ class Api::V1::ReportsController < Api::BaseController
|
||||
end
|
||||
|
||||
def report_params
|
||||
params.permit(:account_id, :comment, status_ids: [])
|
||||
params.permit(:account_id, :comment, :forward, status_ids: [])
|
||||
end
|
||||
end
|
||||
|
@@ -1,30 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::SearchController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
RESULTS_LIMIT = 5
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:search' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@search = Search.new(search_results)
|
||||
@search = Search.new(search)
|
||||
render json: @search, serializer: REST::SearchSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search
|
||||
search_results.tap do |search|
|
||||
search[:statuses].keep_if do |status|
|
||||
begin
|
||||
authorize status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_results
|
||||
SearchService.new.call(
|
||||
params[:q],
|
||||
RESULTS_LIMIT,
|
||||
resolving_search?,
|
||||
truthy_param?(:resolve),
|
||||
current_account
|
||||
)
|
||||
end
|
||||
|
||||
def resolving_search?
|
||||
params[:resolve] == 'true'
|
||||
end
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :authorize_if_got_token
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -71,12 +71,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def authorize_if_got_token
|
||||
request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
|
||||
doorkeeper_authorize! :read if request_token
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::FavouritesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::MutesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_conversation
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
@@ -11,12 +11,18 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
|
||||
def create
|
||||
StatusPin.create!(account: current_account, status: @status)
|
||||
distribute_add_activity!
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
pin = StatusPin.find_by(account: current_account, status: @status)
|
||||
pin&.destroy!
|
||||
|
||||
if pin
|
||||
pin.destroy!
|
||||
distribute_remove_activity!
|
||||
end
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
@@ -25,4 +31,24 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
end
|
||||
|
||||
def distribute_add_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::AddSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
|
||||
def distribute_remove_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::RemoveSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
|
||||
end
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :authorize_if_got_token
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
@@ -68,12 +68,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def authorize_if_got_token
|
||||
request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
|
||||
doorkeeper_authorize! :read if request_token
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class Api::V1::Statuses::ReblogsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
@@ -3,22 +3,27 @@
|
||||
class Api::V1::StatusesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :authorize_if_got_token, except: [:create, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy]
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy]
|
||||
before_action :require_user!, except: [:show, :context, :card]
|
||||
before_action :set_status, only: [:show, :context, :card]
|
||||
|
||||
respond_to :json
|
||||
|
||||
# This API was originally unlimited, pagination cannot be introduced without
|
||||
# breaking backwards-compatibility. Arbitrarily high number to cover most
|
||||
# conversations as quasi-unlimited, it would be too much work to render more
|
||||
# than this anyway
|
||||
CONTEXT_LIMIT = 4_096
|
||||
|
||||
def show
|
||||
cached = Rails.cache.read(@status.cache_key)
|
||||
@status = cached unless cached.nil?
|
||||
@status = cache_collection([@status], Status).first
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def context
|
||||
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(current_account)
|
||||
descendants_results = @status.descendants(current_account)
|
||||
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(CONTEXT_LIMIT, current_account)
|
||||
descendants_results = @status.descendants(CONTEXT_LIMIT, current_account)
|
||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
||||
loaded_descendants = cache_collection(descendants_results, Status)
|
||||
|
||||
@@ -76,11 +81,6 @@ class Api::V1::StatusesController < Api::BaseController
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def authorize_if_got_token
|
||||
request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
|
||||
doorkeeper_authorize! :read if request_token
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
||||
|
26
app/controllers/api/v1/suggestions_controller.rb
Normal file
26
app/controllers/api/v1/suggestions_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::SuggestionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
before_action :set_accounts
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
PotentialFriendshipTracker.remove(current_account.id, params[:id])
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_accounts
|
||||
@accounts = PotentialFriendshipTracker.get(current_account.id, limit: limit_param(DEFAULT_ACCOUNTS_LIMIT))
|
||||
end
|
||||
end
|
63
app/controllers/api/v1/timelines/direct_controller.rb
Normal file
63
app/controllers/api/v1/timelines/direct_controller.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::DirectController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_direct_statuses
|
||||
end
|
||||
|
||||
def cached_direct_statuses
|
||||
cache_collection direct_statuses, Status
|
||||
end
|
||||
|
||||
def direct_statuses
|
||||
direct_timeline_statuses
|
||||
end
|
||||
|
||||
def direct_timeline_statuses
|
||||
# this query requires built in pagination.
|
||||
Status.as_direct_timeline(
|
||||
current_account,
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
true # returns array of cache_ids object
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_direct_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_direct_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user