Compare commits
1987 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
8e9911962d | ||
|
3ebe03b729 | ||
|
6bc07d3de3 | ||
|
fbc6375fde | ||
|
0352c40e99 | ||
|
e5d8166a12 | ||
|
07ea625cb2 | ||
|
27101f1beb | ||
|
1823cf435a | ||
|
6a50329a9c | ||
|
b17e571bc2 | ||
|
476e79b8e3 | ||
|
19d3317a69 | ||
|
fe6941e28e | ||
|
38600b2792 | ||
|
b0407ece42 | ||
|
9b3d8ee346 | ||
|
b3d7ad958f | ||
|
552d22bec9 | ||
|
7c33da45f0 | ||
|
9815be2a44 | ||
|
bebaa6eced | ||
|
616f53eea8 | ||
|
61d3ecc805 | ||
|
4bb3e4eeba | ||
|
784c7510d7 | ||
|
6c54d2e583 | ||
|
96c942e8ab | ||
|
aafe55af81 | ||
|
fd49d5603a | ||
|
1c6fc0e4ce | ||
|
92e7815d1d | ||
|
9d97054fe6 | ||
|
cc796298c9 | ||
|
7fd66cf2fe | ||
|
6e9e0c14e6 | ||
|
0aa810f9c8 | ||
|
3888a12c79 | ||
|
cfa68907ae | ||
|
488584bfc1 | ||
|
0717d9b3e6 | ||
|
6e4046fc3f | ||
|
f0c939c431 | ||
|
ebadfe0ab7 | ||
|
94f15338c3 | ||
|
db33a53ee8 | ||
|
7de6d269d2 | ||
|
684001d729 | ||
|
292f3cd7e0 | ||
|
a3d4f1bd93 | ||
|
633426b261 | ||
|
f486ef2666 | ||
|
d2dee6ea43 | ||
|
967e70663f | ||
|
b7e65a004f | ||
|
3a3475450e | ||
|
057db0ecd0 | ||
|
11436358b4 | ||
|
0e0a9e716c | ||
|
45682f876d | ||
|
4413d81d7f | ||
|
5a2c7bd4ce | ||
|
5c8ca024ef | ||
|
d8b2f89d33 | ||
|
d5f490b1a2 | ||
|
c75ca0525b | ||
|
6f2d88dd28 | ||
|
daa59dd454 | ||
|
72d939b69f | ||
|
97b3d0cd56 | ||
|
fd7f0732fe | ||
|
eb5ac23434 | ||
|
a5143df303 | ||
|
2aca22b8ea | ||
|
7db0f8dcb2 | ||
|
49cc0eb3e7 | ||
|
b9c76e2edb | ||
|
2559d9166c | ||
|
32e8a87830 | ||
|
636acb5712 | ||
|
b406e3cc4c | ||
|
43577e9f59 | ||
|
ecfa1c3f3b | ||
|
b3af3f9f8c | ||
|
d5091387c6 | ||
|
178f718a9b | ||
|
0e1b0f2747 | ||
|
468523f4ad | ||
|
2076c557c9 | ||
|
d40c9140e8 | ||
|
632178d754 | ||
|
291feba6f1 | ||
|
63f0979799 | ||
|
ec13cfa4f9 | ||
|
cdd5ef691b | ||
|
c743b5e1fd | ||
|
dfaa219f88 | ||
|
e6543d5fc4 | ||
|
813c5f2f52 | ||
|
82d9ade7a6 | ||
|
875d943c18 | ||
|
334a446313 | ||
|
ecacb15cd5 | ||
|
eb6ec3d068 | ||
|
f303a954e6 | ||
|
395a57d03d | ||
|
0f699a4280 | ||
|
5e5f36c216 | ||
|
a767ef85fa | ||
|
0db47196fb | ||
|
c30e492587 | ||
|
97c02c3389 | ||
|
4453c9a9f5 | ||
|
b9c612b561 | ||
|
d841af4e80 | ||
|
01d6aa0397 | ||
|
c567c87453 | ||
|
47ecd652d3 | ||
|
04fa4eb7f9 | ||
|
cdad7977fc | ||
|
0b3f1ec62a | ||
|
b110cc542f | ||
|
cdacac8c6c | ||
|
eb605141ff | ||
|
1e1d788757 | ||
|
1df453aff6 | ||
|
f7c909e290 | ||
|
7481ae1bcb | ||
|
cb3b0c1a0f | ||
|
ca0e8be20c | ||
|
83ffc4dc07 | ||
|
d6fe0954e3 | ||
|
ebb8c89207 | ||
|
0060f98847 | ||
|
1a72813b53 | ||
|
c3f9c74719 | ||
|
35a8cafa35 | ||
|
f4ca116ea8 | ||
|
5b45c1646a | ||
|
887cd94e96 | ||
|
d2f56d1cbc | ||
|
d0b4709b2a | ||
|
6e0659c838 | ||
|
3406e30526 | ||
|
76f360c625 | ||
|
a3202f61af | ||
|
4ec1771165 | ||
|
3d9b8847d2 | ||
|
b9f59ebcc6 | ||
|
e648ef0bfb | ||
|
db3ed498b0 | ||
|
901fc48aae | ||
|
3caf0ba923 | ||
|
6e83b5e719 | ||
|
b32a1d5754 | ||
|
9d53a38a44 | ||
|
e528114c53 | ||
|
cf7fbf2c56 | ||
|
91e5b0dfdb | ||
|
3b60832214 | ||
|
259181c41a | ||
|
510df0ac55 | ||
|
98936bfcdf | ||
|
5c82c2b75f | ||
|
0fea700c7b | ||
|
2c8e3fbbfb | ||
|
b982d549f4 | ||
|
909a6d4661 | ||
|
3f3de38075 | ||
|
c267acfcf7 | ||
|
ab625c57ce | ||
|
e756c4f5ec | ||
|
a0bbeafb04 | ||
|
2f079573ed | ||
|
b2820c3913 | ||
|
adc38078dd | ||
|
dae0af1fd2 | ||
|
5b8d0ad71b | ||
|
233258c61b | ||
|
9c8aad612e | ||
|
6dfeb64326 | ||
|
427beb4177 | ||
|
838f255fc2 | ||
|
1e02ba111a | ||
|
66126f3021 | ||
|
293972f716 | ||
|
9c8e602163 | ||
|
846cd4e838 | ||
|
0de82dd316 | ||
|
dcfc9b2204 | ||
|
b01ab55ed8 | ||
|
dd4ef69839 | ||
|
d4f80824f7 | ||
|
034fab39ab | ||
|
0df6442636 | ||
|
245816ab27 | ||
|
63819c848d | ||
|
a9abe89093 | ||
|
798b0fc5af | ||
|
8fcfcddc8f | ||
|
d68df88d4e | ||
|
c8580eb806 | ||
|
f41e70ca38 | ||
|
7b8ecde32d | ||
|
8cb4561145 | ||
|
1607bb445a | ||
|
33c39784e4 | ||
|
669fe9ee06 | ||
|
b35406b700 | ||
|
8e33fc29d7 | ||
|
1de6c52545 | ||
|
34fa305a00 | ||
|
bb4d005a83 | ||
|
df1ce2350c | ||
|
e5c65b3067 | ||
|
7d16bb379d | ||
|
0401a24558 | ||
|
4aea3f88a6 | ||
|
41e6c8b151 | ||
|
813e650729 | ||
|
1664e52cbb | ||
|
dce869dfc7 | ||
|
09a94b575e | ||
|
d43944143a | ||
|
81cec35dbf | ||
|
c155d843f4 | ||
|
3d640dc8ac | ||
|
6db034a866 | ||
|
17bf3363ac | ||
|
dcf1139ebd | ||
|
9619b7f727 | ||
|
66be6475b6 | ||
|
9e0985d9e4 | ||
|
cf14f4945a | ||
|
4c14ff659b | ||
|
dd6f9a1b82 | ||
|
3f07f1b2b1 | ||
|
44245926f1 | ||
|
8811778b55 | ||
|
1eab53ee10 | ||
|
7be3131240 | ||
|
198a9a4fa4 | ||
|
ec36df97c4 | ||
|
c8969dca35 | ||
|
1e3b1d7211 | ||
|
0698c610a6 | ||
|
629fae8b3b | ||
|
c30e6433de | ||
|
cea5597722 | ||
|
48d77ea1eb | ||
|
efec507230 | ||
|
54edb4b853 | ||
|
6c81f9d6e5 | ||
|
472df24579 | ||
|
0d1215e82f | ||
|
e77cc032c2 | ||
|
67559361e8 | ||
|
4a73615193 | ||
|
bdcc9e2ceb | ||
|
3816943e6b | ||
|
b39d512ade | ||
|
04046a4983 | ||
|
a4c500176b | ||
|
1aad015bbb | ||
|
94fba44eec | ||
|
721460a59b | ||
|
45b595cdca | ||
|
aad3df6afc | ||
|
1023f52eaa | ||
|
596dab06e9 | ||
|
4f0597d579 | ||
|
2bbf987a0a | ||
|
af00220d79 | ||
|
9239e4ce4d | ||
|
06f26e09b4 | ||
|
331263270b | ||
|
283a5ec1a4 | ||
|
550ff677da | ||
|
da77f65c46 | ||
|
9e2ff3ef71 | ||
|
b9d241c6f5 | ||
|
56af04dbb4 | ||
|
60944d5dca | ||
|
081f907f90 | ||
|
f29918e707 | ||
|
af10c9fbff | ||
|
8f8e677630 | ||
|
4931eac280 | ||
|
881856553e | ||
|
0a6b098668 | ||
|
0ef9d45d05 | ||
|
a6a206ef85 | ||
|
bbff144004 | ||
|
47d48fed8d | ||
|
3018043fc2 | ||
|
c2bee07dbc | ||
|
a345479de2 | ||
|
08f00df94b | ||
|
ab71cf4593 | ||
|
c450ddb613 | ||
|
15b886a6f0 | ||
|
4819e2913d | ||
|
72e662bb0d | ||
|
7d7844a47f | ||
|
f2cbfb2eb3 | ||
|
3f333a8d31 | ||
|
bc077018b8 | ||
|
90712d4293 | ||
|
6867681c7c | ||
|
bdc8b4fd91 | ||
|
2ff7146b6d | ||
|
c7908e2d09 | ||
|
c9d04f1c39 | ||
|
9e15eeec63 | ||
|
3c45d3963a | ||
|
baa8b82179 | ||
|
4b460bc571 | ||
|
7ca173be47 | ||
|
1ae5d49a71 | ||
|
a12572e074 | ||
|
dabc309ca3 | ||
|
1caf11ddcc | ||
|
95f018a3d4 | ||
|
a4caa7eb62 | ||
|
7c2d84910c | ||
|
b00cc4b9bd | ||
|
dd6ede554f | ||
|
6859d4c028 | ||
|
7d853b514a | ||
|
85c7c42098 | ||
|
8185f98872 | ||
|
5264496240 | ||
|
be75b13d68 | ||
|
9417c9bb8f | ||
|
11bddd31ce | ||
|
dd5cb5085c | ||
|
e7adbf572a | ||
|
13ffa3c59e | ||
|
aec5097d44 | ||
|
1646f622a5 | ||
|
e0cda4a851 | ||
|
d8d2a54741 | ||
|
fa21d004c7 | ||
|
6994664a13 | ||
|
be7ffa2d75 | ||
|
e821c00e74 | ||
|
9b994c4aee | ||
|
4c3dd0b254 | ||
|
672df4ecc0 | ||
|
aefb4719bc | ||
|
4d67bf18fe | ||
|
f09a250a7c | ||
|
9b50a9dd83 | ||
|
2293466edd | ||
|
b6f3869f8d | ||
|
09cffaaf04 | ||
|
334a633c2a | ||
|
8b12e3cc7f | ||
|
d3f46a77c3 | ||
|
a789315361 | ||
|
579c7a88e0 | ||
|
8538170c2d | ||
|
249bdc169c | ||
|
9dd8dff683 | ||
|
a187dcefa1 | ||
|
5d170587e3 | ||
|
37fdddd927 | ||
|
6ec1aa372d | ||
|
2c3544eedd | ||
|
d3b6746173 | ||
|
2a5d1d5a1b | ||
|
6a4e2db661 | ||
|
bfa7f9ebf2 | ||
|
8cc1ed3c55 | ||
|
5e1e466da0 | ||
|
cfe39fb58d | ||
|
a0294c8880 | ||
|
ba8fb2fd0f | ||
|
6fd2e8c3c5 | ||
|
15963a15c6 | ||
|
1b5806b744 | ||
|
1b1e025b41 | ||
|
ab9f1b6e50 | ||
|
b767eb7ff8 | ||
|
0b32338e3f | ||
|
e482595a5d | ||
|
9c04fadec9 | ||
|
390bfec6da | ||
|
c2980d5b17 | ||
|
a75aa62f5b | ||
|
8fd8f81ae7 | ||
|
921cf3e9c8 | ||
|
7dc5035031 | ||
|
2305f7c391 | ||
|
ff7d02b236 | ||
|
1a0df58878 | ||
|
74437c6bff | ||
|
504737e860 | ||
|
af2d22f88c | ||
|
9a5ae09620 | ||
|
f7937d903c | ||
|
6b2be5dbfb | ||
|
69957ed10a | ||
|
d1a78eba15 | ||
|
2db9ccaf3e | ||
|
cecf204bbb | ||
|
fec13735a7 | ||
|
7b8f262840 | ||
|
3f51a22d3b | ||
|
39e7a763ff | ||
|
e95bdec7c5 | ||
|
fcca31350d | ||
|
ee72a39641 | ||
|
f59ed3a4fa | ||
|
7be620775e | ||
|
4c76402ba1 | ||
|
9958eba356 | ||
|
0827c09c44 | ||
|
938cd2875b | ||
|
7876aed134 | ||
|
ce9a5f358e | ||
|
8f527bd588 | ||
|
07994eed00 | ||
|
bab9afaa09 | ||
|
15093f9113 | ||
|
f92d991e52 | ||
|
26402ee2cb | ||
|
f095a9f8a5 | ||
|
0d5d11eeff | ||
|
0397c58b61 | ||
|
884b085f53 | ||
|
2a2698e450 | ||
|
8ecfdd8795 | ||
|
00840f4f2e | ||
|
1cebfed23e | ||
|
649a20ab46 | ||
|
3ac7b353f8 | ||
|
21bb4a6c3b | ||
|
c2af138113 | ||
|
fb8aa2b3ba | ||
|
00f9f16f94 | ||
|
18f69fb964 | ||
|
04c3fb2189 | ||
|
7c03e59338 | ||
|
b88635202f | ||
|
409051c22c | ||
|
9caa90025f | ||
|
c5157ef07b | ||
|
f72ed21cd6 | ||
|
da172a8b1b | ||
|
cf615abbf9 | ||
|
b01a19fe39 | ||
|
c66fe2aeba | ||
|
fbe1115114 | ||
|
e4c761f902 | ||
|
2c6a85832c | ||
|
829e2e8c5d | ||
|
8a716c9e96 | ||
|
80393a23d0 | ||
|
8d23667536 | ||
|
9846806cb5 | ||
|
760cfe328f | ||
|
c1b086a538 | ||
|
696c2c6f2f | ||
|
5927b43c0f | ||
|
871c0d251a | ||
|
11a7507318 | ||
|
d63de55ef8 | ||
|
72bb3e03fd | ||
|
f391a4673a | ||
|
143b77e10d | ||
|
4cbb638604 | ||
|
3534e115e5 | ||
|
ea958cae7f | ||
|
10e9a9a3f9 | ||
|
6e9eda5331 | ||
|
4c23544714 | ||
|
74e5078795 | ||
|
110227ac5e | ||
|
f26758dc01 | ||
|
23792f5a7c | ||
|
fe5b66aa08 | ||
|
93d4192a67 | ||
|
d5acf4275f | ||
|
412ea87306 | ||
|
774b8661bc | ||
|
c7d2619ab1 | ||
|
2edfdab6e6 | ||
|
4edf9d849f | ||
|
10489b4e4a | ||
|
40c45f5dd9 | ||
|
efec02f153 | ||
|
116b8a6363 | ||
|
ad892dbc0c | ||
|
075d6a1e13 | ||
|
54a04e3658 | ||
|
462c30e26c | ||
|
2a04bdc87a | ||
|
ca7ea1aba9 | ||
|
f814661fca | ||
|
e33c28a6d8 | ||
|
e120d09c98 | ||
|
4fcbb1f838 | ||
|
a855956185 | ||
|
5b9ae7981e | ||
|
5f22c0189d | ||
|
26d26644ac | ||
|
3c6503038e | ||
|
96e9ed13de | ||
|
6df8bd277b | ||
|
4e75f0d889 | ||
|
a2aeacbfee | ||
|
b7370ac8ba | ||
|
ccdd5a9576 | ||
|
40be4ea239 | ||
|
3d47154c20 | ||
|
d0a217eb92 | ||
|
81c1303cd6 | ||
|
4b8e4dca26 | ||
|
10cdad3e7d | ||
|
d9a1fb134a | ||
|
fdea173237 | ||
|
4e1bf082ce | ||
|
b1c8a702a4 | ||
|
820099813f | ||
|
2ebe4ff568 | ||
|
61bfce5aa9 | ||
|
dd7ef0dc41 | ||
|
dcbc1af38a | ||
|
81c41d8681 | ||
|
ec3be87a2b | ||
|
b42c018bb8 | ||
|
c9fd6f386c | ||
|
1b5d26735e | ||
|
a3e53bd442 | ||
|
8eb6d171e6 | ||
|
5942347407 | ||
|
22db947225 | ||
|
5d408fd9aa | ||
|
47579ec58c | ||
|
3363a05539 | ||
|
87f10d476c | ||
|
41c3389d76 | ||
|
e7a5a188ef | ||
|
71384b2ef9 | ||
|
d1d465347a | ||
|
5eba129b0f | ||
|
021a83ead4 | ||
|
5ee45fa571 | ||
|
61a06eb328 | ||
|
df605f0f8b | ||
|
029786442a | ||
|
9d1f8b9d6a | ||
|
400616813e | ||
|
724be2d5fe | ||
|
76da330155 | ||
|
ab60aa2266 | ||
|
0bbd5789b5 | ||
|
fae71b653a | ||
|
dfcd2834f9 | ||
|
09e86ef90b | ||
|
9ba7d526a0 | ||
|
94e233e7b2 | ||
|
ac53736814 | ||
|
8c0e78ae43 | ||
|
26ab702304 | ||
|
7ef8482568 | ||
|
559fd08845 | ||
|
202942a76f | ||
|
c3e355388a | ||
|
d4c4820c03 | ||
|
e05606c8d0 | ||
|
161f72cce3 | ||
|
8ccb3b96ab | ||
|
e9ee249fd5 | ||
|
4b6cd1dfdb | ||
|
b9ec3b7e7c | ||
|
9b247c3d88 | ||
|
c7cc806251 | ||
|
82b4cf4acb | ||
|
3e7a541e09 | ||
|
93aafa8549 | ||
|
bb85043f46 | ||
|
e1fcad34a9 | ||
|
155ba8fd3a | ||
|
e44f03bc71 | ||
|
970297a138 | ||
|
29abc9438c | ||
|
f91284d230 | ||
|
feadf7553d | ||
|
ea33cdc30b | ||
|
579e85f606 | ||
|
ea144ba302 | ||
|
4f04981dde | ||
|
990cea471e | ||
|
0913351dcf | ||
|
57a794d8eb | ||
|
a5e0cf2450 | ||
|
a46ba4a8f5 | ||
|
c71874b84c | ||
|
53b2b1b238 | ||
|
634b71ed1d | ||
|
3d378ed0b4 | ||
|
7e0c00a555 | ||
|
f0bb2c6d1e | ||
|
13bb1ddc7f | ||
|
fdb65dcbee | ||
|
4e2f2fab73 | ||
|
6e186b9c77 | ||
|
ff9d344d4c | ||
|
b3c44e95a9 | ||
|
8c0dd33ce4 | ||
|
12874eafa6 | ||
|
afb593b44e | ||
|
296bfa23aa | ||
|
534da4f24f | ||
|
62a9da62a6 | ||
|
58eea59864 | ||
|
c7de92e0df | ||
|
c1633eeb0f | ||
|
f93f306053 | ||
|
e67fc997dc | ||
|
3e01a7e677 | ||
|
0f92119ceb | ||
|
b7d47c2aef | ||
|
6270f9ce34 | ||
|
e54cc15cbd | ||
|
2654f3be82 | ||
|
9004151e34 | ||
|
6884dd79ba | ||
|
f9075577e4 | ||
|
50d38d7605 | ||
|
aa803153e2 | ||
|
f2233c3e25 | ||
|
73890c3cac | ||
|
e1798d0eb0 | ||
|
4f0b638cda | ||
|
bb96ba13cf | ||
|
5bf4838e2f | ||
|
bdf573d140 | ||
|
97a48f237d | ||
|
6654c30033 | ||
|
f49339ca9c | ||
|
994d948c39 | ||
|
f5e228ad2e | ||
|
92cb451da8 | ||
|
55bee84c97 | ||
|
a248be4fce | ||
|
8b43d6bf9c | ||
|
b8adb4d7fa | ||
|
4ba33f99fc | ||
|
7905739c2a | ||
|
6a6a62f13f | ||
|
aa8fa71df6 | ||
|
7874c6d630 | ||
|
7bf0afb1dc | ||
|
2f8bfb3d38 | ||
|
4115043dc7 | ||
|
7062cb764f | ||
|
9891ff80f9 | ||
|
7232cdf7e8 | ||
|
9f97c8c750 | ||
|
edadc93757 | ||
|
a6ea7e282f | ||
|
e5c0aa6493 | ||
|
02744f29ef | ||
|
a31d24ee18 | ||
|
6957c5b5c6 | ||
|
696bcff6bf | ||
|
f52ce92f2b | ||
|
c80046a77b | ||
|
ebf5a06084 | ||
|
23e854cb91 | ||
|
de105d64d5 | ||
|
07d93716aa | ||
|
88b5e0b703 | ||
|
32fa312b2a | ||
|
1306d637a2 | ||
|
462b3752e4 | ||
|
029f2c4545 | ||
|
b3e7beb7c5 | ||
|
a549d1ae6b | ||
|
467456f7a1 | ||
|
2374d63536 | ||
|
117eb3b2bc | ||
|
de985a30bc | ||
|
06d905f415 | ||
|
0ad41be0f3 | ||
|
d6f5dbff3e | ||
|
1e665a0bf4 | ||
|
ef16089c6d | ||
|
4b4ea1f929 | ||
|
45af29912f | ||
|
9075c90c46 | ||
|
63a2566007 | ||
|
43cad817e8 | ||
|
ed4c754fff | ||
|
1e0c7a0afc | ||
|
3a3b556065 | ||
|
9244f6b628 | ||
|
ff26b72333 | ||
|
6803935c4d | ||
|
3757546f1b | ||
|
a677ac8384 | ||
|
bdbfb10cff | ||
|
4d661e1183 | ||
|
dd28b557ae | ||
|
0e0f18ce7c | ||
|
7964bfccdb | ||
|
3c515f2cd2 | ||
|
db73ac92d7 | ||
|
4cd82d442e | ||
|
311871eefc | ||
|
a929f7e6ac | ||
|
cf51e07bde | ||
|
8d6c3cd48a | ||
|
3817704806 | ||
|
d4c6bf770d | ||
|
399f9f4a4e | ||
|
f2390e2803 | ||
|
dbaa6a0e13 | ||
|
7bf7ed6123 | ||
|
a390abdefb | ||
|
c1bc5e14eb | ||
|
4b911fea03 | ||
|
1fcdaafa6f | ||
|
f24b81e27f | ||
|
e01966f7b8 | ||
|
dcb9497148 | ||
|
4f2513337f | ||
|
015269914e | ||
|
bbdcfd6baf | ||
|
f0d6550f16 | ||
|
8400bee3b1 | ||
|
bc1f9dc24b | ||
|
cdc349a2d1 | ||
|
c2c93f8cd6 | ||
|
9fc082ea81 | ||
|
4c7a9adb98 | ||
|
030e5cec58 | ||
|
716f4cb11c | ||
|
a5a07da892 | ||
|
72108b20e2 | ||
|
767117f9b0 | ||
|
fb7f06a752 | ||
|
0b4006fc47 | ||
|
0ccd47f413 | ||
|
02f896c12e | ||
|
bb4c3831b2 | ||
|
3267e4a785 | ||
|
89b988cab5 | ||
|
4d42a38954 | ||
|
8387b3928e | ||
|
afa52e4d63 | ||
|
8949aad030 | ||
|
c0c7af2194 | ||
|
f5382ec085 | ||
|
407073d7a2 | ||
|
7f4375822a | ||
|
719ab720a7 | ||
|
b11ac88692 | ||
|
681c33d1f4 | ||
|
7f35947d8e | ||
|
68941d4dfa | ||
|
1d2616b79b | ||
|
d4b097a88c | ||
|
902c5cf7ca | ||
|
b15f790221 | ||
|
a47c2e8890 | ||
|
a3202fd51e | ||
|
1cceefce33 | ||
|
033f970af3 | ||
|
d1c3e35d3f | ||
|
a6328fc1b1 | ||
|
35b868eeca | ||
|
695439775e | ||
|
05cd37097c | ||
|
bd915d9398 | ||
|
8c45cd0e36 | ||
|
3fbf1bf35a | ||
|
cd9b2ab2f7 | ||
|
de397f3bc1 | ||
|
72bd73f605 | ||
|
1896a154f5 | ||
|
1618b68bfa | ||
|
c1f201c49a | ||
|
8d224ad23b | ||
|
e2685ccc81 | ||
|
c42092ba7a | ||
|
999170d898 | ||
|
37430a3401 | ||
|
0fa9dd8527 | ||
|
489d162477 | ||
|
9008ab3407 | ||
|
87b96f8d33 | ||
|
a49be27145 | ||
|
27b2355738 | ||
|
eeb5923e89 | ||
|
a9067167bb | ||
|
a9a0c854e1 | ||
|
0c7c188c45 | ||
|
c2753fdfb4 | ||
|
c29c20ab3c | ||
|
880a5eb25c | ||
|
e48d3bfd01 | ||
|
5abb3d8150 | ||
|
c45a75ad34 | ||
|
3567ac3d3e | ||
|
43f868de3d | ||
|
f41590912d | ||
|
056b5ed72f | ||
|
1764c32b9e | ||
|
b21ab498f8 | ||
|
1c6c6b271c | ||
|
e6c81a635b | ||
|
f93de3a516 | ||
|
e19eefe219 | ||
|
8784bd79d0 | ||
|
31366334cb | ||
|
425acecfdb | ||
|
29f314a502 | ||
|
cc68d1945b | ||
|
7bacdd718a | ||
|
958fe0f7db | ||
|
e670fa2af6 | ||
|
a3d93e8bbe | ||
|
7a889a8e12 | ||
|
d081d4a422 | ||
|
34ccc058fa | ||
|
7f9a353b94 | ||
|
31490e0d6c | ||
|
ca45bd0361 | ||
|
63baab088d | ||
|
2b9721d1b3 | ||
|
617208053c | ||
|
4aa6cd66fc | ||
|
1c6cbdd4e4 | ||
|
f8212da329 | ||
|
4122a837fa | ||
|
5fa2dd6e65 | ||
|
307f3e0dd7 | ||
|
fc4c74660b | ||
|
caf938562e | ||
|
ce3a371eee | ||
|
8781a8e203 | ||
|
37c832cdf7 | ||
|
f68fa930ea | ||
|
007ab330e6 | ||
|
794781d121 | ||
|
91cacb1e8f | ||
|
46f5d3a2e9 | ||
|
76318f8830 | ||
|
852bda3d32 | ||
|
0324f807f4 | ||
|
864e3f8d9c | ||
|
102466ac58 | ||
|
63b77f2320 | ||
|
8fecd80108 | ||
|
348d6f5e75 | ||
|
00df69bc89 | ||
|
7a549f830e | ||
|
3f82d8b979 | ||
|
9fe6cfca48 | ||
|
ebd2dde688 | ||
|
6e1261f277 | ||
|
91d548f7e6 | ||
|
76eda2fc21 | ||
|
1c1819a78a | ||
|
8b2cad5637 | ||
|
2d6128672f | ||
|
185b41beb4 | ||
|
2083000027 | ||
|
18d3fa953b | ||
|
f76e71825d | ||
|
6bf6d35637 | ||
|
9c03fd9cae | ||
|
34c8a46d7d | ||
|
26949607d2 | ||
|
e7c0d87d98 | ||
|
6d106d3943 | ||
|
a37cf9548c | ||
|
5e6acf9601 | ||
|
b52a5e6bd6 | ||
|
bb194ddb3c | ||
|
a38b34c37a | ||
|
1921ab40ea | ||
|
976c18aa5f | ||
|
4cddef1cea | ||
|
cbe94b88e2 | ||
|
275c5b51ed | ||
|
f85dbe83c8 | ||
|
a9c326b200 | ||
|
92f1c474f3 | ||
|
a6d02cff36 | ||
|
be94f9e35d | ||
|
e282580101 | ||
|
331f0953e9 | ||
|
133b892e0d | ||
|
60da49f856 | ||
|
d1d94216d1 | ||
|
bf50e3e5ae | ||
|
a978b88997 | ||
|
6dd5eac7fc | ||
|
968354923e | ||
|
59ddf81a45 | ||
|
3a7106f05a | ||
|
5c7a4f0b32 | ||
|
0e09048537 | ||
|
7362469d89 | ||
|
1273fbf86e | ||
|
a27879c0cf | ||
|
049cea30b0 | ||
|
b342c81c17 | ||
|
ead14f5bf0 | ||
|
0a53ca444a | ||
|
f79c10162e | ||
|
60b2b56d38 | ||
|
b6a19e7b89 | ||
|
71bc75e6ac | ||
|
e4fee6c138 | ||
|
7d8e3721ae | ||
|
fb421a1f46 | ||
|
2a9805b987 | ||
|
126f929c39 | ||
|
da42bfadb5 | ||
|
6ad72728f6 | ||
|
64d9c016bd | ||
|
12e7c81dd8 | ||
|
16d0aed403 | ||
|
da9317fa56 | ||
|
be92babd00 | ||
|
e2dd576a1b | ||
|
8f2c91568c | ||
|
98eaa2aa27 | ||
|
42b8220632 | ||
|
a91d968cab | ||
|
646de92781 | ||
|
ae2b722f55 | ||
|
7aeb9168b0 | ||
|
f53ed108b0 | ||
|
285038972b | ||
|
e5563843a2 | ||
|
c972e1ee1f | ||
|
5e8d037e27 | ||
|
ed7dc1704d | ||
|
436ce03772 | ||
|
d821aba002 | ||
|
4ce1540094 | ||
|
67243bda31 | ||
|
8f991831b8 | ||
|
87efa38721 | ||
|
f7301bd5b9 | ||
|
099a3b4eac | ||
|
3d4e21f1ec | ||
|
68dca26a5d | ||
|
1fc096ec75 | ||
|
21c2bc119c | ||
|
d23293c762 | ||
|
138e5a0b1e | ||
|
79dacea962 | ||
|
4e6b5e7879 | ||
|
c0979381a4 | ||
|
676f577e7e | ||
|
c1a8e3d1eb | ||
|
0c44316b22 | ||
|
2211e8d1cd | ||
|
3783cadf2d | ||
|
a071047c13 | ||
|
281f07244b | ||
|
6f34a6a77f | ||
|
e078919f07 | ||
|
7b13e6efc2 | ||
|
3f59238207 | ||
|
eff9416469 | ||
|
6fbb3841a6 | ||
|
d8c4781377 | ||
|
bc6e958229 | ||
|
a6d8d1036a | ||
|
3d403a013d | ||
|
9ca02a00a6 | ||
|
3e8e9c8ae4 | ||
|
7bc1805827 | ||
|
e27f792c24 | ||
|
98fab24bea | ||
|
f566c47dda | ||
|
0190aac240 | ||
|
cc382c5006 | ||
|
946a166791 | ||
|
31cd649041 | ||
|
1585b0c6cc | ||
|
15b43f555d | ||
|
d8ec832806 | ||
|
bab5a18232 | ||
|
a20cf3b64e | ||
|
356df7ae6b | ||
|
8f03fdce7f | ||
|
1fc6cb4997 | ||
|
eb832e88f4 | ||
|
b16b69350e | ||
|
da6fa029f6 | ||
|
94ad0706f5 | ||
|
bf8c2c4348 | ||
|
aa58cca040 | ||
|
5cc7cd8518 | ||
|
ff142eb64d | ||
|
500e28442f | ||
|
5bd3715a4c | ||
|
3d13f6ea0c | ||
|
6eefccdacc | ||
|
29a22691d2 | ||
|
d55f207274 | ||
|
cf6fe4f8cb | ||
|
4367443287 | ||
|
8d2b3ada80 | ||
|
f3be605286 | ||
|
aebebdc5d1 | ||
|
05e4728de7 | ||
|
b51945f096 | ||
|
1f2abd8d67 | ||
|
1d9f9352a6 | ||
|
53e42bf91e | ||
|
94d0e012de | ||
|
8fd931dc12 | ||
|
74d10b9b9d | ||
|
2356580cee | ||
|
1840a352f5 | ||
|
c93d0978f2 | ||
|
df4f4e94b3 | ||
|
51b2f789bd | ||
|
947887f261 | ||
|
6f34fdb616 | ||
|
8518d005fd | ||
|
bb911043de | ||
|
da0333f1cb | ||
|
d8a0ee1956 | ||
|
91c71471ab | ||
|
98eacb2238 | ||
|
80c13bf0ef | ||
|
e17c2e5da5 | ||
|
4a618908e8 | ||
|
a208e7d655 | ||
|
c1b9ae7fc2 | ||
|
dc8a6244fc | ||
|
0f52e42c2d | ||
|
85af2405cf | ||
|
47ace633dc | ||
|
85d5518b6b | ||
|
5104bd7988 | ||
|
3e425b51fd | ||
|
37dbfa4cd7 | ||
|
0d23c81662 | ||
|
b436b31d5a | ||
|
72133fbed6 | ||
|
abbdacedc5 | ||
|
ddd3251912 | ||
|
605e2a417c | ||
|
f8fe394e7a | ||
|
2a545e0fb1 | ||
|
ce812466c7 | ||
|
47bf7a8047 | ||
|
85d405c810 | ||
|
f596a413ef | ||
|
9e53fe5c29 | ||
|
3690f04e4a | ||
|
f3e8bc9f8f | ||
|
dcf0530218 | ||
|
47338bc13d | ||
|
6fb9726b99 | ||
|
8015fd7600 | ||
|
4919b89ab8 | ||
|
2925372ff4 | ||
|
778430b54a | ||
|
5282ba862a | ||
|
0464602978 | ||
|
9b03cf0ddd | ||
|
cdff1da901 | ||
|
1a065fb146 | ||
|
022008a2a6 | ||
|
a3715598cc | ||
|
1be48dd805 | ||
|
6384041d17 | ||
|
140e73bc82 | ||
|
e3fae6f52c | ||
|
65d8c73bae | ||
|
177dd8bb53 | ||
|
380b20eed6 | ||
|
c207b4bb33 | ||
|
b87eb8ea14 | ||
|
8902e265b4 | ||
|
b8ea28d6d0 | ||
|
f741673638 | ||
|
0a0b9a271a | ||
|
7d2b4186c3 | ||
|
90689190a3 | ||
|
8acadeea76 | ||
|
75c6513c67 | ||
|
73540ffe6b | ||
|
92bb166246 | ||
|
8cf8ce4ac0 | ||
|
0f1b1d78b1 | ||
|
d3bbef27e7 | ||
|
f0634ba876 | ||
|
1d68fe1a60 | ||
|
6bd6dcf6df | ||
|
28d2920472 | ||
|
34bfea8bbf | ||
|
2d91944285 | ||
|
0026ba2751 | ||
|
b623dd12c1 | ||
|
722d152082 | ||
|
7623766241 | ||
|
e34c5a3503 | ||
|
004672aa6c | ||
|
ad4a28f4f6 | ||
|
d8ae3efec3 | ||
|
cd81a1c52a | ||
|
dcf73ddeff | ||
|
d81b706f12 | ||
|
30fa5fe1a4 | ||
|
7a7bfa5170 | ||
|
e969c78645 | ||
|
7adac1bc51 | ||
|
e859d6f259 | ||
|
a0880edc6e | ||
|
61fcdbbf7e | ||
|
43af695ba1 | ||
|
facd90e7a6 | ||
|
6201f96b8a | ||
|
c26cea262b | ||
|
1f1d6bf2a0 | ||
|
4c06d1cb24 | ||
|
2985d08951 | ||
|
66ca7157db | ||
|
4addf051d4 | ||
|
ab914ce6d5 | ||
|
6a4b224397 | ||
|
6adbd114c1 | ||
|
037f96c5ae | ||
|
f54dca06a9 | ||
|
370fa70924 | ||
|
5be1214c26 | ||
|
f7a30e2fae | ||
|
3f815b2052 | ||
|
defe4f9bc3 | ||
|
943775fd90 | ||
|
42844df966 | ||
|
b0fe58dc69 | ||
|
e07b57852e | ||
|
7c7c18fdea | ||
|
a84664026e | ||
|
02a0fd5b64 | ||
|
6505a42be0 | ||
|
e674608d10 | ||
|
c7af8cbc90 | ||
|
9475fbae78 | ||
|
00e61d6807 | ||
|
19084d3c6c | ||
|
e014bf8ed0 | ||
|
f6e2309e70 | ||
|
9d2154c4ab | ||
|
1dfd27a028 | ||
|
b97ebaf620 | ||
|
8ee2eb5d2e | ||
|
20b647020b | ||
|
3eedad2737 | ||
|
ce7c0def88 | ||
|
dab8fc4584 | ||
|
8a597f0138 | ||
|
3363f2f4d6 | ||
|
c7f2d6af55 | ||
|
e878ddb7c0 | ||
|
336f0b0823 | ||
|
3ea3f24a02 | ||
|
d567a382e3 | ||
|
18fe77084f | ||
|
dc253ea234 | ||
|
9304114b57 | ||
|
1fd5251376 | ||
|
edddc7c791 | ||
|
10768aa204 | ||
|
e98559c3ff | ||
|
2212dc4aaa | ||
|
e1fdac3e9a | ||
|
1162f61ca3 | ||
|
39ea5c0e2e | ||
|
509b0cfafc | ||
|
fda5c699c2 | ||
|
cb7ee4698f | ||
|
d010e270e6 | ||
|
d1e08bd38c | ||
|
dbccdcc1b1 | ||
|
5c63523972 | ||
|
de4681b2be | ||
|
a132332b86 | ||
|
b25e42a77f | ||
|
5236a62861 | ||
|
0f155829b7 | ||
|
84dda45df9 | ||
|
9c7505489f | ||
|
75cad1d9d6 | ||
|
2cc3111a77 | ||
|
bf811e4d4a | ||
|
d6774d2ca3 | ||
|
bd669e3907 | ||
|
1a4860a57a | ||
|
41fa53253c | ||
|
c00ead8a72 | ||
|
e49dc6a06e | ||
|
0e12a8dab9 | ||
|
3652a39de0 | ||
|
79335e46fd | ||
|
7c6e02aaf3 | ||
|
7f55430652 | ||
|
8235623362 | ||
|
83435c49ea | ||
|
93de41b39b | ||
|
b1d4b74a44 | ||
|
bfdf47bc98 | ||
|
33f669a5f8 | ||
|
3576fa0d59 | ||
|
1dcfb90202 | ||
|
22cf18e16f | ||
|
0ebe7d6d23 | ||
|
23081bb299 | ||
|
4c7fe48c40 | ||
|
499cc7b803 | ||
|
7db98aa70e | ||
|
e031fd60ad | ||
|
bc4fad9e22 | ||
|
5ac4d677e9 | ||
|
b42bdd80e8 | ||
|
76fa9d2488 | ||
|
dfc43a6d3d | ||
|
67bc58dd60 | ||
|
2d39560dc1 | ||
|
c49ff7395e | ||
|
e0ada97770 | ||
|
3a2003ba86 | ||
|
9a81be0d37 | ||
|
5e2c5e95b6 | ||
|
34a93ccf57 | ||
|
922fb74197 | ||
|
7bf2d6cb06 | ||
|
11e5c965c3 | ||
|
34157d118c | ||
|
7b92950f1c | ||
|
97d7028c31 | ||
|
a7f2961621 | ||
|
00dda99789 | ||
|
2e27ce3b61 | ||
|
2c10c5a069 | ||
|
bd4dd4c4a0 | ||
|
7d33b60f3f | ||
|
aecce5694b | ||
|
0e4ca51951 | ||
|
dde043f6cd | ||
|
c778a60e4f | ||
|
c347327d54 | ||
|
fd328cf6e8 | ||
|
7b473d7514 | ||
|
dff576b75d | ||
|
52ae83d008 | ||
|
5aacd9d4c7 | ||
|
d24d3fa283 | ||
|
c8a226f61c | ||
|
7a281c477a | ||
|
91c789ec63 | ||
|
9ead3d1cdb | ||
|
402c19a924 | ||
|
b5e8994844 | ||
|
4bd327a0c5 | ||
|
184325077e | ||
|
8236f942ff | ||
|
8963f8c3c2 | ||
|
5e41c26203 | ||
|
45837c533e | ||
|
3fa8512474 | ||
|
0e20de9f89 | ||
|
24d645b7d0 | ||
|
7b23f79d41 | ||
|
3b4095cf1b | ||
|
28cbfb9f10 | ||
|
189a06d2a2 | ||
|
450441fc11 | ||
|
b619362a36 | ||
|
425d02287a | ||
|
2e429c0c25 | ||
|
e0e12b0fee | ||
|
62ca37884a | ||
|
f9180823bc | ||
|
4b0c667c09 | ||
|
1b732cad61 | ||
|
ecef03bb15 | ||
|
9642601126 | ||
|
3836d293a1 | ||
|
0734e1fe33 | ||
|
44cb08297c | ||
|
bd21afb5ed | ||
|
ef80ad17b3 | ||
|
9ea4f37e78 | ||
|
c48772fd3f | ||
|
860e257a68 | ||
|
902d9e34b4 |
7
.babelrc
7
.babelrc
@@ -14,14 +14,16 @@
|
||||
],
|
||||
"plugins": [
|
||||
"syntax-dynamic-import",
|
||||
"transform-object-rest-spread",
|
||||
["transform-object-rest-spread", { "useBuiltIns": true }],
|
||||
"transform-decorators-legacy",
|
||||
"transform-class-properties",
|
||||
[
|
||||
"react-intl",
|
||||
{
|
||||
"messagesDir": "./build/messages"
|
||||
}
|
||||
]
|
||||
],
|
||||
"preval"
|
||||
],
|
||||
"env": {
|
||||
"development": {
|
||||
@@ -43,6 +45,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"transform-react-inline-elements",
|
||||
[
|
||||
"transform-runtime",
|
||||
{
|
||||
|
@@ -1,21 +1,37 @@
|
||||
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
|
||||
scss-lint:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.rb"
|
||||
- "**.js"
|
||||
- "**.scss"
|
||||
exclude_paths:
|
||||
exclude_patterns:
|
||||
- spec/
|
||||
- vendor/asset
|
||||
|
@@ -2,8 +2,8 @@
|
||||
.env.*
|
||||
public/system
|
||||
public/assets
|
||||
public/packs
|
||||
node_modules
|
||||
storybook
|
||||
neo4j
|
||||
vendor/bundle
|
||||
.DS_Store
|
||||
|
129
.env.nanobox
129
.env.nanobox
@@ -11,11 +11,31 @@ DB_NAME=gonano
|
||||
DB_PASS=$DATA_DB_PASS
|
||||
DB_PORT=5432
|
||||
|
||||
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||
|
||||
# Optional ElasticSearch configuration
|
||||
# ES_ENABLED=true
|
||||
# ES_HOST=localhost
|
||||
# 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
|
||||
@@ -29,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
|
||||
@@ -60,14 +90,14 @@ 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
|
||||
# PAPERCLIP_ROOT_URL=/system
|
||||
|
||||
# Optional asset host for multi-server setups
|
||||
# CDN_HOST=assets.example.com
|
||||
# CDN_HOST=https://assets.example.com
|
||||
|
||||
# S3 (optional)
|
||||
# S3_ENABLED=true
|
||||
@@ -89,6 +119,23 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
# S3_ENDPOINT=
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# 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 if you want to use Cloudfront or Cloudflare in front
|
||||
# S3_CLOUDFRONT_HOST=
|
||||
|
||||
@@ -101,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=
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# Service dependencies
|
||||
# You may set REDIS_URL instead for more advanced options
|
||||
# You may also set REDIS_NAMESPACE to share Redis between multiple Mastodon servers
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
# You may set DATABASE_URL instead for more advanced options
|
||||
@@ -8,12 +9,17 @@ DB_USER=postgres
|
||||
DB_NAME=postgres
|
||||
DB_PASS=
|
||||
DB_PORT=5432
|
||||
# Optional ElasticSearch configuration
|
||||
# ES_ENABLED=true
|
||||
# ES_HOST=localhost
|
||||
# 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
|
||||
@@ -26,11 +32,21 @@ LOCAL_HTTPS=true
|
||||
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||
PAPERCLIP_SECRET=
|
||||
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||
SECRET_KEY_BASE=
|
||||
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 `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
|
||||
#
|
||||
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||
VAPID_PRIVATE_KEY=
|
||||
VAPID_PUBLIC_KEY=
|
||||
|
||||
# Registrations
|
||||
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||
# SINGLE_USER_MODE=true
|
||||
@@ -45,7 +61,7 @@ OTP_SECRET=
|
||||
# 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
|
||||
@@ -58,14 +74,14 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
#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
|
||||
# PAPERCLIP_ROOT_URL=/system
|
||||
|
||||
# Optional asset host for multi-server setups
|
||||
# CDN_HOST=assets.example.com
|
||||
# CDN_HOST=https://assets.example.com
|
||||
|
||||
# S3 (optional)
|
||||
# S3_ENABLED=true
|
||||
@@ -87,6 +103,23 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# S3_ENDPOINT=
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# 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 if you want to use Cloudfront or Cloudflare in front
|
||||
# S3_CLOUDFRONT_HOST=
|
||||
|
||||
@@ -105,3 +138,75 @@ 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
|
||||
|
||||
# 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.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=
|
||||
|
@@ -1,4 +1,3 @@
|
||||
# Federation
|
||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
LOCAL_HTTPS=true
|
||||
OTP_SECRET=100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4
|
||||
|
130
.eslintrc.yml
130
.eslintrc.yml
@@ -1,42 +1,38 @@
|
||||
---
|
||||
root: true
|
||||
|
||||
env:
|
||||
browser: true
|
||||
node: false
|
||||
node: true
|
||||
es6: true
|
||||
jest: true
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
plugins:
|
||||
- react
|
||||
- jsx-a11y
|
||||
- import
|
||||
|
||||
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:
|
||||
|
||||
no-cond-assign: error
|
||||
no-console: warn
|
||||
no-irregular-whitespace: error
|
||||
no-unreachable: error
|
||||
valid-typeof: error
|
||||
consistent-return: error
|
||||
dot-notation: error
|
||||
eqeqeq: error
|
||||
no-fallthrough: error
|
||||
no-unused-expressions: error
|
||||
strict: off
|
||||
no-catch-shadow: error
|
||||
indent:
|
||||
- warn
|
||||
- 2
|
||||
brace-style: warn
|
||||
comma-dangle:
|
||||
- error
|
||||
- always-multiline
|
||||
comma-spacing:
|
||||
- warn
|
||||
- before: false
|
||||
@@ -44,24 +40,74 @@ rules:
|
||||
comma-style:
|
||||
- warn
|
||||
- last
|
||||
consistent-return: error
|
||||
dot-notation: error
|
||||
eqeqeq: error
|
||||
indent:
|
||||
- warn
|
||||
- 2
|
||||
jsx-quotes:
|
||||
- error
|
||||
- prefer-single
|
||||
no-catch-shadow: error
|
||||
no-cond-assign: error
|
||||
no-console:
|
||||
- warn
|
||||
- allow:
|
||||
- error
|
||||
- warn
|
||||
no-fallthrough: error
|
||||
no-irregular-whitespace: error
|
||||
no-mixed-spaces-and-tabs: warn
|
||||
no-nested-ternary: warn
|
||||
no-trailing-spaces: warn
|
||||
semi: error
|
||||
no-undef: error
|
||||
no-unreachable: error
|
||||
no-unused-expressions: error
|
||||
no-unused-vars:
|
||||
- error
|
||||
- vars: all
|
||||
args: after-used
|
||||
ignoreRestSiblings: true
|
||||
object-curly-spacing:
|
||||
- error
|
||||
- always
|
||||
padded-blocks:
|
||||
- error
|
||||
- classes: always
|
||||
comma-dangle:
|
||||
quotes:
|
||||
- error
|
||||
- always-multiline
|
||||
- single
|
||||
semi: error
|
||||
strict: off
|
||||
valid-typeof: error
|
||||
|
||||
react/jsx-wrap-multilines: error
|
||||
react/jsx-boolean-value: error
|
||||
react/jsx-closing-bracket-location:
|
||||
- error
|
||||
- line-aligned
|
||||
react/jsx-curly-spacing: error
|
||||
react/jsx-equals-spacing: error
|
||||
react/jsx-first-prop-new-line:
|
||||
- error
|
||||
- multiline-multiprop
|
||||
react/jsx-indent:
|
||||
- error
|
||||
- 2
|
||||
react/jsx-no-bind: error
|
||||
react/self-closing-comp: error
|
||||
react/prop-types: error
|
||||
react/jsx-no-duplicate-props: error
|
||||
react/jsx-no-undef: error
|
||||
react/jsx-tag-spacing: error
|
||||
react/jsx-uses-react: error
|
||||
react/jsx-uses-vars: error
|
||||
react/jsx-wrap-multilines: error
|
||||
react/no-multi-comp: off
|
||||
react/no-string-refs: error
|
||||
react/prop-types: error
|
||||
react/self-closing-comp: error
|
||||
|
||||
jsx-a11y/accessible-emoji: warn
|
||||
jsx-a11y/alt-text: warn
|
||||
jsx-a11y/anchor-has-content: warn
|
||||
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||
jsx-a11y/aria-props: warn
|
||||
@@ -72,17 +118,37 @@ rules:
|
||||
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/label-has-for: 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: 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
|
||||
|
14
.gitattributes
vendored
Normal file
14
.gitattributes
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
* text=auto eol=lf
|
||||
*.eot -text
|
||||
*.gif -text
|
||||
*.gz -text
|
||||
*.ico -text
|
||||
*.jpg -text
|
||||
*.mp3 -text
|
||||
*.ogg -text
|
||||
*.png -text
|
||||
*.ttf -text
|
||||
*.webm -text
|
||||
*.woff -text
|
||||
*.woff2 -text
|
||||
spec/fixtures/requests/** -text !eol
|
32
.github/CODEOWNERS
vendored
Normal file
32
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# CODEOWNERS for tootsuite/mastodon
|
||||
|
||||
# Translators
|
||||
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
|
||||
# /app/javascript/mastodon/locales/fr.json @żelipapą
|
||||
# /app/views/user_mailer/*.fr.html.erb @żelipapą
|
||||
# /app/views/user_mailer/*.fr.text.erb @żelipapą
|
||||
# /config/locales/*.fr.yml @żelipapą
|
||||
# /config/locales/fr.yml @żelipapą
|
||||
|
||||
# Polish
|
||||
/app/javascript/mastodon/locales/pl.json @m4sk1n
|
||||
/app/views/user_mailer/*.pl.html.erb @m4sk1n
|
||||
/app/views/user_mailer/*.pl.text.erb @m4sk1n
|
||||
/config/locales/*.pl.yml @m4sk1n
|
||||
/config/locales/pl.yml @m4sk1n
|
||||
|
||||
# French
|
||||
/app/javascript/mastodon/locales/fr.json @aldarone
|
||||
/app/javascript/mastodon/locales/whitelist_fr.json @aldarone
|
||||
/app/views/user_mailer/*.fr.html.erb @aldarone
|
||||
/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
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,6 +20,7 @@ coverage
|
||||
public/system
|
||||
public/assets
|
||||
public/packs
|
||||
public/packs-test
|
||||
.env
|
||||
.env.production
|
||||
node_modules/
|
||||
@@ -33,6 +34,7 @@ config/deploy/*
|
||||
|
||||
# Ignore IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Ignore postgres + redis volume optionally created by docker-compose
|
||||
postgres
|
||||
|
@@ -14,7 +14,6 @@ node_modules/
|
||||
public/assets/
|
||||
public/system/
|
||||
spec/
|
||||
storybook/
|
||||
tmp/
|
||||
.vagrant/
|
||||
vendor/bundle/
|
||||
|
@@ -6,3 +6,4 @@ plugins:
|
||||
- last 2 versions
|
||||
- IE >= 11
|
||||
- iOS >= 9
|
||||
postcss-object-fit-images: {}
|
||||
|
120
.rubocop.yml
120
.rubocop.yml
@@ -1,14 +1,46 @@
|
||||
Rails:
|
||||
Enabled: true
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'db/**/*'
|
||||
- 'app/views/**/*'
|
||||
- 'config/**/*'
|
||||
- 'bin/*'
|
||||
- 'Rakefile'
|
||||
- 'node_modules/**/*'
|
||||
- 'Vagrantfile'
|
||||
- 'vendor/**/*'
|
||||
- 'lib/json_ld/*'
|
||||
|
||||
Style/PerlBackrefs:
|
||||
AutoCorrect: false
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
|
||||
Layout/AccessModifierIndentation:
|
||||
EnforcedStyle: indent
|
||||
|
||||
Layout/EmptyLineAfterMagicComment:
|
||||
Enabled: false
|
||||
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
EnforcedStyle: space
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 100
|
||||
|
||||
Metrics/BlockLength:
|
||||
Max: 35
|
||||
Exclude:
|
||||
- 'lib/tasks/**/*'
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Max: 2
|
||||
Max: 3
|
||||
|
||||
Metrics/ClassLength:
|
||||
CountComments: false
|
||||
Max: 300
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 25
|
||||
|
||||
Metrics/LineLength:
|
||||
AllowURI: true
|
||||
@@ -16,37 +48,30 @@ Metrics/LineLength:
|
||||
|
||||
Metrics/MethodLength:
|
||||
CountComments: false
|
||||
Max: 10
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 100
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Max: 3
|
||||
|
||||
Metrics/ClassLength:
|
||||
CountComments: false
|
||||
Max: 200
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 15
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 55
|
||||
|
||||
Metrics/ModuleLength:
|
||||
CountComments: false
|
||||
Max: 200
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 10
|
||||
|
||||
Metrics/ParameterLists:
|
||||
Max: 4
|
||||
Max: 5
|
||||
CountKeywordArgs: true
|
||||
|
||||
Style/AccessModifierIndentation:
|
||||
EnforcedStyle: indent
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 20
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
Style/CollectionMethods:
|
||||
Enabled: true
|
||||
@@ -62,36 +87,25 @@ Style/DoubleNegation:
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: true
|
||||
|
||||
Style/SpaceInsideHashLiteralBraces:
|
||||
EnforcedStyle: space
|
||||
|
||||
Style/TrailingCommaInLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
||||
Style/RegexpLiteral:
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
Style/Lambda:
|
||||
Enabled: false
|
||||
|
||||
Style/GuardClause:
|
||||
Style/PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
'%i': '()'
|
||||
'%w': '()'
|
||||
|
||||
Style/PerlBackrefs:
|
||||
AutoCorrect: false
|
||||
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
Rails/HasAndBelongsToMany:
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'db/**/*'
|
||||
- 'app/views/**/*'
|
||||
- 'config/**/*'
|
||||
- 'bin/*'
|
||||
- 'Rakefile'
|
||||
- 'node_modules/**/*'
|
||||
- 'Vagrantfile'
|
||||
- 'vendor/**/*'
|
||||
Style/TrailingCommaInLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
@@ -1 +1 @@
|
||||
2.4.1
|
||||
2.5.0
|
||||
|
@@ -2,4 +2,3 @@ node_modules/
|
||||
.cache/
|
||||
docs/
|
||||
spec/
|
||||
storybook/
|
||||
|
38
.travis.yml
38
.travis.yml
@@ -3,9 +3,15 @@ cache:
|
||||
bundler: true
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- node_modules
|
||||
- public/assets
|
||||
- public/packs-test
|
||||
- tmp/cache/babel-loader
|
||||
dist: trusty
|
||||
sudo: false
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -17,39 +23,37 @@ env:
|
||||
- RAILS_ENV=test
|
||||
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
||||
- PARALLEL_TEST_PROCESSORS=2
|
||||
- "PATH=$HOME:$PATH"
|
||||
|
||||
addons:
|
||||
postgresql: 9.4
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- trusty-media
|
||||
- trusty-media
|
||||
- sourceline: deb https://dl.yarnpkg.com/debian/ stable main
|
||||
key_url: https://dl.yarnpkg.com/debian/pubkey.gpg
|
||||
packages:
|
||||
- ffmpeg
|
||||
- g++-6
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
- ffmpeg
|
||||
- libicu-dev
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
- yarn
|
||||
|
||||
rvm:
|
||||
- 2.3.4
|
||||
- 2.4.1
|
||||
- 2.4.2
|
||||
- 2.5.0
|
||||
|
||||
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++"
|
||||
- ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
|
||||
|
||||
script:
|
||||
- bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||
- npm test
|
||||
- bundle exec i18n-tasks unused
|
||||
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||
- yarn test
|
||||
- bundle exec i18n-tasks check-normalized && bundle exec i18n-tasks unused
|
||||
|
46
.yarnclean
Normal file
46
.yarnclean
Normal file
@@ -0,0 +1,46 @@
|
||||
# test directories
|
||||
__tests__
|
||||
test
|
||||
tests
|
||||
powered-test
|
||||
|
||||
# asset directories
|
||||
docs
|
||||
doc
|
||||
website
|
||||
images
|
||||
# assets
|
||||
|
||||
# examples
|
||||
example
|
||||
examples
|
||||
|
||||
# code coverage directories
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# build scripts
|
||||
Makefile
|
||||
Gulpfile.js
|
||||
Gruntfile.js
|
||||
|
||||
# configs
|
||||
.tern-project
|
||||
.gitattributes
|
||||
.editorconfig
|
||||
.*ignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
.flowconfig
|
||||
.documentup.json
|
||||
.yarn-metadata.json
|
||||
.*.yml
|
||||
*.yml
|
||||
|
||||
# misc
|
||||
*.gz
|
||||
*.md
|
||||
|
||||
# for specific ignore
|
||||
!.svgo.yml
|
||||
|
9
Aptfile
9
Aptfile
@@ -1,5 +1,10 @@
|
||||
protobuf-compiler
|
||||
libprotobuf-dev
|
||||
ffmpeg
|
||||
libicu[0-9][0-9]
|
||||
libicu-dev
|
||||
libidn11
|
||||
libidn11-dev
|
||||
libpq-dev
|
||||
libprotobuf-dev
|
||||
libxdamage1
|
||||
libxfixes3
|
||||
protobuf-compiler
|
||||
|
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/
|
72
Dockerfile
72
Dockerfile
@@ -1,22 +1,30 @@
|
||||
FROM ruby:2.4.1-alpine
|
||||
FROM ruby:2.5.0-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 \
|
||||
RAILS_SERVE_STATIC_FILES=true \
|
||||
ARG UID=991
|
||||
ARG GID=991
|
||||
|
||||
ENV RAILS_SERVE_STATIC_FILES=true \
|
||||
RAILS_ENV=production NODE_ENV=production
|
||||
|
||||
ARG YARN_VERSION=1.3.2
|
||||
ARG YARN_DOWNLOAD_SHA256=6cfe82e530ef0837212f13e45c1565ba53f5199eec2527b85ecbcd88bf26821d
|
||||
ARG LIBICONV_VERSION=1.15
|
||||
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||
|
||||
EXPOSE 3000 4000
|
||||
|
||||
WORKDIR /mastodon
|
||||
|
||||
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
|
||||
&& apk -U upgrade \
|
||||
RUN apk -U upgrade \
|
||||
&& apk add -t build-dependencies \
|
||||
build-base \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
icu-dev \
|
||||
libidn-dev \
|
||||
libressl \
|
||||
libtool \
|
||||
postgresql-dev \
|
||||
protobuf-dev \
|
||||
python \
|
||||
@@ -25,30 +33,52 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
|
||||
ffmpeg \
|
||||
file \
|
||||
git \
|
||||
imagemagick@edge \
|
||||
icu-libs \
|
||||
imagemagick \
|
||||
libidn \
|
||||
libpq \
|
||||
libxml2 \
|
||||
libxslt \
|
||||
nodejs-npm@edge \
|
||||
nodejs@edge \
|
||||
nodejs \
|
||||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
&& npm install -g npm@3 && npm install -g yarn \
|
||||
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 "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 \
|
||||
&& cd /tmp/src/libiconv-$LIBICONV_VERSION \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make -j$(getconf _NPROCESSORS_ONLN)\
|
||||
&& make install \
|
||||
&& libtool --finish /usr/local/lib \
|
||||
&& cd /mastodon \
|
||||
&& rm -rf /tmp/* /var/cache/apk/*
|
||||
|
||||
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
||||
COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
|
||||
|
||||
RUN bundle install --deployment --without test development \
|
||||
&& yarn --ignore-optional --pure-lockfile
|
||||
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 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 chmod +x /usr/local/bin/run
|
||||
RUN chown -R mastodon:mastodon /mastodon
|
||||
|
||||
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/run"]
|
||||
USER mastodon
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
93
Gemfile
93
Gemfile
@@ -1,103 +1,138 @@
|
||||
# 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 'puma', '~> 3.8'
|
||||
gem 'rails', '~> 5.0'
|
||||
gem 'uglifier', '~> 3.2'
|
||||
gem 'puma', '~> 3.10'
|
||||
gem 'rails', '~> 5.1.4'
|
||||
|
||||
gem 'hamlit-rails', '~> 0.2'
|
||||
gem 'pg', '~> 0.20'
|
||||
gem 'pghero', '~> 1.7'
|
||||
gem 'dotenv-rails', '~> 2.2'
|
||||
|
||||
gem 'aws-sdk', '~> 2.9'
|
||||
gem 'aws-sdk', '~> 2.10', require: false
|
||||
gem 'fog-core', '~> 1.45'
|
||||
gem 'fog-local', '~> 0.4', require: false
|
||||
gem 'fog-openstack', '~> 0.1', require: false
|
||||
gem 'paperclip', '~> 5.1'
|
||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||
gem 'streamio-ffmpeg', '~> 3.0'
|
||||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.5'
|
||||
gem 'bootsnap'
|
||||
gem 'cld3', '~> 3.1'
|
||||
gem 'devise', '~> 4.2'
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.5'
|
||||
gem 'iso-639'
|
||||
gem 'chewy', '~> 5.0'
|
||||
gem 'cld3', '~> 3.2.0'
|
||||
gem 'devise', '~> 4.4'
|
||||
gem 'devise-two-factor', '~> 3.0'
|
||||
|
||||
gem 'devise_pam_authenticatable2', '~> 8.0', install_if: -> { ENV['PAM_ENABLED'] == 'true' }
|
||||
gem 'net-ldap', '~> 0.10'
|
||||
gem 'omniauth-cas', '~> 1.1'
|
||||
gem 'omniauth-saml', '~> 1.10'
|
||||
gem 'omniauth', '~> 1.2'
|
||||
|
||||
gem 'doorkeeper', '~> 4.2'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'goldfinger', '~> 1.2'
|
||||
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.0'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 0.99'
|
||||
gem 'kaminari', '~> 1.0'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'kaminari', '~> 1.1'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'nokogiri', '~> 1.7'
|
||||
gem 'oj', '~> 3.0'
|
||||
gem 'mime-types', '~> 3.1'
|
||||
gem 'nokogiri', '~> 1.8'
|
||||
gem 'nsa', '~> 0.2'
|
||||
gem 'oj', '~> 3.3'
|
||||
gem 'ostatus2', '~> 2.0'
|
||||
gem 'ox', '~> 2.5'
|
||||
gem 'rabl', '~> 0.13'
|
||||
gem 'ox', '~> 2.8'
|
||||
gem 'pundit', '~> 1.1'
|
||||
gem 'premailer-rails'
|
||||
gem 'rack-attack', '~> 5.0'
|
||||
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||
gem 'rack-timeout', '~> 0.4'
|
||||
gem 'rails-i18n', '~> 5.0'
|
||||
gem 'rails-settings-cached', '~> 0.6'
|
||||
gem 'redis', '~> 3.3', 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 'ruby-progressbar', '~> 1.4'
|
||||
gem 'sanitize', '~> 4.4'
|
||||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-scheduler', '~> 2.1'
|
||||
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||
gem 'sidekiq-bulk', '~>0.1.1'
|
||||
gem 'simple-navigation', '~> 4.0'
|
||||
gem 'simple_form', '~> 3.4'
|
||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||
gem 'statsd-instrument', '~> 2.1'
|
||||
gem 'strong_migrations'
|
||||
gem 'tty-command'
|
||||
gem 'tty-prompt'
|
||||
gem 'twitter-text', '~> 1.14'
|
||||
gem 'tzinfo-data', '~> 1.2017'
|
||||
gem 'webpacker', '~> 1.2'
|
||||
gem 'webpacker', '~> 3.0'
|
||||
gem 'webpush'
|
||||
|
||||
gem 'json-ld-preloaded', '~> 2.2.1'
|
||||
gem 'rdf-normalize', '~> 0.3.1'
|
||||
|
||||
group :development, :test do
|
||||
gem 'fabrication', '~> 2.16'
|
||||
gem 'fabrication', '~> 2.18'
|
||||
gem 'fuubar', '~> 2.2'
|
||||
gem 'i18n-tasks', '~> 0.9', require: false
|
||||
gem 'pry-rails', '~> 0.3'
|
||||
gem 'rspec-rails', '~> 3.6'
|
||||
gem 'rspec-rails', '~> 3.7'
|
||||
end
|
||||
|
||||
group :production, :test do
|
||||
gem 'private_address_check', '~> 0.4.1'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara', '~> 2.14'
|
||||
gem 'capybara', '~> 2.15'
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'faker', '~> 1.7'
|
||||
gem 'microformats2', '~> 3.0'
|
||||
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 'parallel_tests', '~> 2.17'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'active_record_query_trace', '~> 1.5'
|
||||
gem 'annotate', '~> 2.7'
|
||||
gem 'better_errors', '~> 2.1'
|
||||
gem 'better_errors', '~> 2.4'
|
||||
gem 'binding_of_caller', '~> 0.7'
|
||||
gem 'bullet', '~> 5.5'
|
||||
gem 'letter_opener', '~> 1.4'
|
||||
gem 'letter_opener_web', '~> 1.3'
|
||||
gem 'rubocop', '~> 0.48', require: false
|
||||
gem 'brakeman', '~> 3.6', require: false
|
||||
gem 'bundler-audit', '~> 0.5', require: false
|
||||
gem 'scss_lint', '~> 0.53', require: false
|
||||
gem 'memory_profiler'
|
||||
gem 'rubocop', require: false
|
||||
gem 'brakeman', '~> 4.0', require: false
|
||||
gem 'bundler-audit', '~> 0.6', require: false
|
||||
gem 'scss_lint', '~> 0.55', require: false
|
||||
|
||||
gem 'capistrano', '~> 3.8'
|
||||
gem 'capistrano-rails', '~> 1.2'
|
||||
gem 'capistrano', '~> 3.10'
|
||||
gem 'capistrano-rails', '~> 1.3'
|
||||
gem 'capistrano-rbenv', '~> 2.1'
|
||||
gem 'capistrano-yarn', '~> 2.0'
|
||||
end
|
||||
|
||||
group :production do
|
||||
gem 'lograge', '~> 0.5'
|
||||
gem 'lograge', '~> 0.7'
|
||||
gem 'redis-rails', '~> 5.0'
|
||||
end
|
||||
|
621
Gemfile.lock
621
Gemfile.lock
@@ -1,136 +1,154 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
nio4r (>= 1.2, < 3.0)
|
||||
actioncable (5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (~> 0.6.1)
|
||||
actionmailer (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activejob (= 5.0.3)
|
||||
actionmailer (5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
actionview (= 5.1.4)
|
||||
activejob (= 5.1.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
actionpack (5.1.4)
|
||||
actionview (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
rack (~> 2.0)
|
||||
rack-test (~> 0.6.3)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
actionview (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_model_serializers (0.10.7)
|
||||
actionpack (>= 4.1, < 6)
|
||||
activemodel (>= 4.1, < 6)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_record_query_trace (1.5.4)
|
||||
activejob (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
activejob (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
activerecord (5.0.3)
|
||||
activemodel (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
arel (~> 7.0)
|
||||
activesupport (5.0.3)
|
||||
activemodel (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
activerecord (5.1.4)
|
||||
activemodel (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
arel (~> 8.0)
|
||||
activesupport (5.1.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.1)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
airbrussh (1.2.0)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
airbrussh (1.3.0)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
annotate (2.7.1)
|
||||
annotate (2.7.2)
|
||||
activerecord (>= 3.2, < 6.0)
|
||||
rake (>= 10.4, < 12.0)
|
||||
arel (7.1.4)
|
||||
rake (>= 10.4, < 13.0)
|
||||
arel (8.0.0)
|
||||
ast (2.3.0)
|
||||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
av (0.9.0)
|
||||
cocaine (~> 0.5.3)
|
||||
aws-sdk (2.9.21)
|
||||
aws-sdk-resources (= 2.9.21)
|
||||
aws-sdk-core (2.9.21)
|
||||
aws-sdk (2.10.100)
|
||||
aws-sdk-resources (= 2.10.100)
|
||||
aws-sdk-core (2.10.100)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.9.21)
|
||||
aws-sdk-core (= 2.9.21)
|
||||
aws-sigv4 (1.0.0)
|
||||
aws-sdk-resources (2.10.100)
|
||||
aws-sdk-core (= 2.10.100)
|
||||
aws-sigv4 (1.0.2)
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.1.1)
|
||||
better_errors (2.4.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootsnap (0.2.14)
|
||||
bootsnap (1.1.5)
|
||||
msgpack (~> 1.0)
|
||||
brakeman (3.6.1)
|
||||
brakeman (4.0.1)
|
||||
browser (2.5.2)
|
||||
builder (3.2.3)
|
||||
bullet (5.5.1)
|
||||
bullet (5.6.1)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.10.0)
|
||||
bundler-audit (0.5.0)
|
||||
bundler-audit (0.6.0)
|
||||
bundler (~> 1.2)
|
||||
thor (~> 0.18)
|
||||
capistrano (3.8.1)
|
||||
capistrano (3.10.0)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
sshkit (>= 1.9.0)
|
||||
capistrano-bundler (1.2.0)
|
||||
capistrano-bundler (1.3.0)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.2)
|
||||
capistrano-rails (1.2.3)
|
||||
capistrano-rails (1.3.1)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rbenv (2.1.1)
|
||||
capistrano-rbenv (2.1.3)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.3)
|
||||
capistrano-yarn (2.0.2)
|
||||
capistrano (~> 3.0)
|
||||
capybara (2.14.0)
|
||||
capybara (2.16.1)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
case_transform (0.2)
|
||||
activesupport
|
||||
charlock_holmes (0.7.5)
|
||||
chewy (5.0.0)
|
||||
activesupport (>= 4.0)
|
||||
elasticsearch (>= 2.0.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.3.8)
|
||||
cld3 (3.1.2)
|
||||
cld3 (3.2.2)
|
||||
ffi (>= 1.1.0, < 1.10.0)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
coderay (1.1.1)
|
||||
coderay (1.1.2)
|
||||
colorize (0.8.1)
|
||||
concurrent-ruby (1.0.5)
|
||||
connection_pool (2.2.1)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.2)
|
||||
crass (1.0.3)
|
||||
css_parser (1.6.0)
|
||||
addressable
|
||||
debug_inspector (0.0.3)
|
||||
devise (4.3.0)
|
||||
devise (4.4.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-two-factor (3.0.0)
|
||||
activesupport
|
||||
devise-two-factor (3.0.2)
|
||||
activesupport (< 5.2)
|
||||
attr_encrypted (>= 1.3, < 4, != 2)
|
||||
devise (~> 4.0)
|
||||
railties
|
||||
railties (< 5.2)
|
||||
rotp (~> 2.0)
|
||||
devise_pam_authenticatable2 (8.0.1)
|
||||
devise (>= 4.0.0)
|
||||
rpam2 (~> 3.0)
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20170404)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (4.2.5)
|
||||
doorkeeper (4.2.6)
|
||||
railties (>= 4.2)
|
||||
dotenv (2.2.1)
|
||||
dotenv-rails (2.2.1)
|
||||
@@ -140,26 +158,54 @@ GEM
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
elasticsearch (6.0.1)
|
||||
elasticsearch-api (= 6.0.1)
|
||||
elasticsearch-transport (= 6.0.1)
|
||||
elasticsearch-api (6.0.1)
|
||||
multi_json
|
||||
elasticsearch-dsl (0.1.5)
|
||||
elasticsearch-transport (6.0.1)
|
||||
faraday
|
||||
multi_json
|
||||
encryptor (3.0.0)
|
||||
erubis (2.7.0)
|
||||
et-orbi (1.0.4)
|
||||
equatable (0.5.0)
|
||||
erubi (1.7.0)
|
||||
et-orbi (1.0.8)
|
||||
tzinfo
|
||||
execjs (2.7.0)
|
||||
fabrication (2.16.1)
|
||||
faker (1.7.3)
|
||||
excon (0.59.0)
|
||||
fabrication (2.18.0)
|
||||
faker (1.8.4)
|
||||
i18n (~> 0.5)
|
||||
faraday (0.14.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fast_blank (1.0.0)
|
||||
fastimage (2.1.1)
|
||||
ffi (1.9.18)
|
||||
fog-core (1.45.0)
|
||||
builder
|
||||
excon (~> 0.58)
|
||||
formatador (~> 0.2)
|
||||
fog-json (1.0.2)
|
||||
fog-core (~> 1.0)
|
||||
multi_json (~> 1.10)
|
||||
fog-local (0.4.0)
|
||||
fog-core (~> 1.27)
|
||||
fog-openstack (0.1.22)
|
||||
fog-core (>= 1.40)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
formatador (0.2.5)
|
||||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
globalid (0.4.0)
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
goldfinger (1.2.0)
|
||||
addressable (~> 2.4)
|
||||
http (~> 2.0)
|
||||
nokogiri (~> 1.6)
|
||||
hamlit (2.8.1)
|
||||
goldfinger (2.1.0)
|
||||
addressable (~> 2.5)
|
||||
http (~> 3.0)
|
||||
nokogiri (~> 1.8)
|
||||
oj (~> 3.0)
|
||||
hamlit (2.8.5)
|
||||
temple (>= 0.8.0)
|
||||
thor
|
||||
tilt
|
||||
@@ -168,48 +214,66 @@ GEM
|
||||
activesupport (>= 4.0.1)
|
||||
hamlit (>= 1.2.0)
|
||||
railties (>= 4.0.1)
|
||||
hashdiff (0.3.4)
|
||||
highline (1.7.8)
|
||||
hamster (3.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
hashdiff (0.3.7)
|
||||
hashie (3.5.7)
|
||||
highline (1.7.10)
|
||||
hiredis (0.6.1)
|
||||
hitimes (1.2.6)
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http (2.2.2)
|
||||
http (3.0.0)
|
||||
addressable (~> 2.3)
|
||||
http-cookie (~> 1.0)
|
||||
http-form_data (~> 1.0.1)
|
||||
http-form_data (>= 2.0.0.pre.pre2, < 3)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
http-form_data (1.0.3)
|
||||
http_accept_language (2.1.0)
|
||||
http-form_data (2.0.0)
|
||||
http_accept_language (2.1.1)
|
||||
http_parser.rb (0.6.0)
|
||||
httplog (0.99.3)
|
||||
httplog (0.99.7)
|
||||
colorize
|
||||
rack
|
||||
i18n (0.8.1)
|
||||
i18n-tasks (0.9.15)
|
||||
i18n (0.9.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (0.9.19)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
easy_translate (>= 0.5.0)
|
||||
erubis
|
||||
erubi
|
||||
highline (>= 1.7.3)
|
||||
i18n
|
||||
parser (>= 2.2.3.0)
|
||||
rainbow (~> 2.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.0)
|
||||
ipaddress (0.8.3)
|
||||
iso-639 (0.2.8)
|
||||
jmespath (1.3.1)
|
||||
json (2.1.0)
|
||||
kaminari (1.0.1)
|
||||
json-ld (2.1.7)
|
||||
multi_json (~> 1.12)
|
||||
rdf (~> 2.2, >= 2.2.8)
|
||||
json-ld-preloaded (2.2.2)
|
||||
json-ld (~> 2.1, >= 2.1.5)
|
||||
multi_json (~> 1.11)
|
||||
rdf (~> 2.2)
|
||||
jsonapi-renderer (0.2.0)
|
||||
jwt (2.1.0)
|
||||
kaminari (1.1.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.0.1)
|
||||
kaminari-activerecord (= 1.0.1)
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-actionview (1.0.1)
|
||||
kaminari-actionview (= 1.1.1)
|
||||
kaminari-activerecord (= 1.1.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-actionview (1.1.1)
|
||||
actionview
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-activerecord (1.0.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-activerecord (1.1.1)
|
||||
activerecord
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-core (1.0.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-core (1.1.1)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.4.1)
|
||||
@@ -219,44 +283,66 @@ GEM
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
link_header (0.0.8)
|
||||
lograge (0.5.1)
|
||||
lograge (0.7.1)
|
||||
actionpack (>= 4, < 5.2)
|
||||
activesupport (>= 4, < 5.2)
|
||||
railties (>= 4, < 5.2)
|
||||
loofah (2.0.3)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.1.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.5)
|
||||
mime-types (>= 1.16, < 4)
|
||||
method_source (0.8.2)
|
||||
microformats2 (3.1.0)
|
||||
mail (2.7.0)
|
||||
mini_mime (>= 0.1.1)
|
||||
mario-redis-lock (1.2.0)
|
||||
redis (~> 3, >= 3.0.5)
|
||||
memory_profiler (0.9.10)
|
||||
method_source (0.9.0)
|
||||
microformats (4.0.7)
|
||||
json
|
||||
nokogiri
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.2)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.10.2)
|
||||
mini_mime (1.0.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
msgpack (1.1.0)
|
||||
multi_json (1.12.1)
|
||||
multi_json (1.12.2)
|
||||
multipart-post (2.0.0)
|
||||
necromancer (0.4.0)
|
||||
net-ldap (0.16.1)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (4.1.0)
|
||||
nio4r (2.0.0)
|
||||
nokogiri (1.7.2)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogumbo (1.4.11)
|
||||
net-ssh (4.2.0)
|
||||
nio4r (2.1.0)
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.4.13)
|
||||
nokogiri
|
||||
oj (3.0.9)
|
||||
openssl (2.0.3)
|
||||
nsa (0.2.4)
|
||||
activesupport (>= 4.2, < 6)
|
||||
concurrent-ruby (~> 1.0.0)
|
||||
sidekiq (>= 3.5.0)
|
||||
statsd-ruby (~> 1.2.0)
|
||||
oj (3.3.10)
|
||||
omniauth (1.8.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-cas (1.1.1)
|
||||
addressable (~> 2.3)
|
||||
nokogiri (~> 1.5)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-saml (1.10.0)
|
||||
omniauth (~> 1.3, >= 1.3.2)
|
||||
ruby-saml (~> 1.7)
|
||||
orm_adapter (0.5.0)
|
||||
ostatus2 (2.0.0)
|
||||
addressable (~> 2.4)
|
||||
http (~> 2.0)
|
||||
nokogiri (~> 1.6)
|
||||
openssl (~> 2.0)
|
||||
ox (2.5.0)
|
||||
paperclip (5.1.0)
|
||||
ostatus2 (2.0.3)
|
||||
addressable (~> 2.5)
|
||||
http (~> 3.0)
|
||||
nokogiri (~> 1.8)
|
||||
ox (2.8.2)
|
||||
paperclip (5.2.1)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
cocaine (~> 0.5.5)
|
||||
@@ -265,46 +351,58 @@ GEM
|
||||
paperclip-av-transcoder (0.6.4)
|
||||
av (~> 0.9.0)
|
||||
paperclip (>= 2.5.2)
|
||||
parallel (1.11.2)
|
||||
parallel_tests (2.14.1)
|
||||
parallel (1.12.0)
|
||||
parallel_tests (2.19.0)
|
||||
parallel
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.20.0)
|
||||
parser (2.4.0.2)
|
||||
ast (~> 2.3)
|
||||
pastel (0.7.2)
|
||||
equatable (~> 0.5.0)
|
||||
tty-color (~> 0.4.0)
|
||||
pg (0.21.0)
|
||||
pghero (1.7.0)
|
||||
activerecord
|
||||
pkg-config (1.2.0)
|
||||
pkg-config (1.2.8)
|
||||
powerpack (0.1.1)
|
||||
pry (0.10.4)
|
||||
premailer (1.11.1)
|
||||
addressable
|
||||
css_parser (>= 1.6.0)
|
||||
htmlentities (>= 4.0.0)
|
||||
premailer-rails (1.10.1)
|
||||
actionmailer (>= 3, < 6)
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
private_address_check (0.4.1)
|
||||
pry (0.11.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
method_source (~> 0.9.0)
|
||||
pry-rails (0.3.6)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (2.0.5)
|
||||
puma (3.8.2)
|
||||
rabl (0.13.1)
|
||||
activesupport (>= 2.3.14)
|
||||
public_suffix (3.0.1)
|
||||
puma (3.11.0)
|
||||
pundit (1.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (2.0.3)
|
||||
rack-attack (5.0.1)
|
||||
rack
|
||||
rack-cors (0.4.1)
|
||||
rack-protection (2.0.0)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rack-proxy (0.6.2)
|
||||
rack
|
||||
rack-test (0.8.2)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-timeout (0.4.2)
|
||||
rails (5.0.3)
|
||||
actioncable (= 5.0.3)
|
||||
actionmailer (= 5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activejob (= 5.0.3)
|
||||
activemodel (= 5.0.3)
|
||||
activerecord (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 5.0.3)
|
||||
rails (5.1.4)
|
||||
actioncable (= 5.1.4)
|
||||
actionmailer (= 5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
actionview (= 5.1.4)
|
||||
activejob (= 5.1.4)
|
||||
activemodel (= 5.1.4)
|
||||
activerecord (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.1.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.2)
|
||||
actionpack (~> 5.x, >= 5.0.1)
|
||||
@@ -318,92 +416,112 @@ GEM
|
||||
rails-i18n (5.0.4)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 5.0)
|
||||
rails-settings-cached (0.6.5)
|
||||
rails-settings-cached (0.6.6)
|
||||
rails (>= 4.2.0)
|
||||
railties (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
railties (5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
rake (11.3.0)
|
||||
redis (3.3.3)
|
||||
redis-actionpack (5.0.1)
|
||||
rake (12.3.0)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rdf (2.2.12)
|
||||
hamster (~> 3.0)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.3.2)
|
||||
rdf (~> 2.0)
|
||||
redis (3.3.5)
|
||||
redis-actionpack (5.0.2)
|
||||
actionpack (>= 4.0, < 6)
|
||||
redis-rack (>= 1, < 3)
|
||||
redis-store (>= 1.1.0, < 1.4.0)
|
||||
redis-activesupport (5.0.2)
|
||||
redis-store (>= 1.1.0, < 2)
|
||||
redis-activesupport (5.0.4)
|
||||
activesupport (>= 3, < 6)
|
||||
redis-store (~> 1.3.0)
|
||||
redis-namespace (1.5.3)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
redis-rack (2.0.2)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
redis-rack (2.0.3)
|
||||
rack (>= 1.5, < 3)
|
||||
redis-store (>= 1.2, < 1.4)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-rails (5.0.2)
|
||||
redis-actionpack (>= 5.0, < 6)
|
||||
redis-activesupport (>= 5.0, < 6)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.3.0)
|
||||
redis (>= 2.2)
|
||||
redis-store (1.4.1)
|
||||
redis (>= 2.2, < 5)
|
||||
request_store (1.3.2)
|
||||
responders (2.4.0)
|
||||
actionpack (>= 4.2.0, < 5.3)
|
||||
railties (>= 4.2.0, < 5.3)
|
||||
rotp (2.1.2)
|
||||
rpam2 (3.1.0)
|
||||
rqrcode (0.10.1)
|
||||
chunky_png (~> 1.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
rspec-core (3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-rails (3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rails (3.7.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-sidekiq (3.0.1)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-sidekiq (3.0.3)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.6.0)
|
||||
rubocop (0.48.1)
|
||||
rspec-support (3.7.0)
|
||||
rubocop (0.51.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
rainbow (>= 2.2.2, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-oembed (0.12.0)
|
||||
ruby-progressbar (1.8.1)
|
||||
rufus-scheduler (3.4.0)
|
||||
ruby-progressbar (1.9.0)
|
||||
ruby-saml (1.7.2)
|
||||
nokogiri (>= 1.5.10)
|
||||
rufus-scheduler (3.4.2)
|
||||
et-orbi (~> 1.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.4.0)
|
||||
sanitize (4.5.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4.1)
|
||||
sass (3.4.24)
|
||||
scss_lint (0.53.0)
|
||||
sass (3.5.3)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
scss_lint (0.56.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.4.20)
|
||||
sidekiq (5.0.0)
|
||||
sass (~> 3.5.3)
|
||||
sidekiq (5.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.3, >= 3.3.3)
|
||||
sidekiq-scheduler (2.1.4)
|
||||
redis (~> 3)
|
||||
redis (>= 3.3.4, < 5)
|
||||
sidekiq-bulk (0.1.1)
|
||||
activesupport
|
||||
sidekiq
|
||||
sidekiq-scheduler (2.1.10)
|
||||
redis (>= 3, < 5)
|
||||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 3)
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (5.0.8)
|
||||
sidekiq-unique-jobs (5.0.10)
|
||||
sidekiq (>= 4.0, <= 6.0)
|
||||
thor (~> 0)
|
||||
simple-navigation (4.0.5)
|
||||
@@ -411,147 +529,198 @@ GEM
|
||||
simple_form (3.5.0)
|
||||
actionpack (> 4, < 5.2)
|
||||
activemodel (> 4, < 5.2)
|
||||
simplecov (0.14.1)
|
||||
simplecov (0.15.1)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.1)
|
||||
slop (3.6.0)
|
||||
simplecov-html (0.10.2)
|
||||
sprockets (3.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.0)
|
||||
sprockets-rails (3.2.1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkit (1.13.1)
|
||||
sshkit (1.15.1)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
statsd-instrument (2.1.2)
|
||||
statsd-ruby (1.2.1)
|
||||
streamio-ffmpeg (3.0.2)
|
||||
multi_json (~> 1.8)
|
||||
strong_migrations (0.1.9)
|
||||
activerecord (>= 3.2.0)
|
||||
temple (0.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.19.4)
|
||||
thor (0.20.0)
|
||||
thread (0.2.2)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.7)
|
||||
twitter-text (1.14.5)
|
||||
tilt (2.0.8)
|
||||
timers (4.1.2)
|
||||
hitimes
|
||||
tty-color (0.4.2)
|
||||
tty-command (0.7.0)
|
||||
pastel (~> 0.7.0)
|
||||
tty-cursor (0.5.0)
|
||||
tty-prompt (0.15.0)
|
||||
necromancer (~> 0.4.0)
|
||||
pastel (~> 0.7.0)
|
||||
timers (~> 4.0)
|
||||
tty-cursor (~> 0.5.0)
|
||||
tty-reader (~> 0.2.0)
|
||||
tty-reader (0.2.0)
|
||||
tty-cursor (~> 0.5.0)
|
||||
tty-screen (~> 0.6.4)
|
||||
wisper (~> 2.0.0)
|
||||
tty-screen (0.6.4)
|
||||
twitter-text (1.14.7)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (1.2.3)
|
||||
tzinfo (1.2.4)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2017.2)
|
||||
tzinfo-data (1.2017.3)
|
||||
tzinfo (>= 1.0.0)
|
||||
uglifier (3.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.4)
|
||||
unicode-display_width (1.2.1)
|
||||
unicode-display_width (1.3.0)
|
||||
uniform_notifier (1.10.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webmock (3.0.1)
|
||||
webmock (3.1.1)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
webpacker (1.2)
|
||||
webpacker (3.0.2)
|
||||
activesupport (>= 4.2)
|
||||
multi_json (~> 1.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 4.2)
|
||||
webpush (0.3.3)
|
||||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
websocket-driver (0.6.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
xpath (2.0.0)
|
||||
websocket-extensions (0.1.3)
|
||||
wisper (2.0.0)
|
||||
xpath (2.1.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_model_serializers (~> 0.10)
|
||||
active_record_query_trace (~> 1.5)
|
||||
addressable (~> 2.5)
|
||||
annotate (~> 2.7)
|
||||
aws-sdk (~> 2.9)
|
||||
better_errors (~> 2.1)
|
||||
aws-sdk (~> 2.10)
|
||||
better_errors (~> 2.4)
|
||||
binding_of_caller (~> 0.7)
|
||||
bootsnap
|
||||
brakeman (~> 3.6)
|
||||
brakeman (~> 4.0)
|
||||
browser
|
||||
bullet (~> 5.5)
|
||||
bundler-audit (~> 0.5)
|
||||
capistrano (~> 3.8)
|
||||
capistrano-rails (~> 1.2)
|
||||
bundler-audit (~> 0.6)
|
||||
capistrano (~> 3.10)
|
||||
capistrano-rails (~> 1.3)
|
||||
capistrano-rbenv (~> 2.1)
|
||||
capistrano-yarn (~> 2.0)
|
||||
capybara (~> 2.14)
|
||||
cld3 (~> 3.1)
|
||||
devise (~> 4.2)
|
||||
capybara (~> 2.15)
|
||||
charlock_holmes (~> 0.7.5)
|
||||
chewy (~> 5.0)
|
||||
cld3 (~> 3.2.0)
|
||||
climate_control (~> 0.2)
|
||||
devise (~> 4.4)
|
||||
devise-two-factor (~> 3.0)
|
||||
devise_pam_authenticatable2 (~> 8.0)
|
||||
doorkeeper (~> 4.2)
|
||||
dotenv-rails (~> 2.2)
|
||||
fabrication (~> 2.16)
|
||||
fabrication (~> 2.18)
|
||||
faker (~> 1.7)
|
||||
fast_blank (~> 1.0)
|
||||
fastimage
|
||||
fog-core (~> 1.45)
|
||||
fog-local (~> 0.4)
|
||||
fog-openstack (~> 0.1)
|
||||
fuubar (~> 2.2)
|
||||
goldfinger (~> 1.2)
|
||||
goldfinger (~> 2.1)
|
||||
hamlit-rails (~> 0.2)
|
||||
hiredis (~> 0.6)
|
||||
htmlentities (~> 4.3)
|
||||
http (~> 2.2)
|
||||
http (~> 3.0)
|
||||
http_accept_language (~> 2.1)
|
||||
httplog (~> 0.99)
|
||||
i18n-tasks (~> 0.9)
|
||||
kaminari (~> 1.0)
|
||||
idn-ruby
|
||||
iso-639
|
||||
json-ld-preloaded (~> 2.2.1)
|
||||
kaminari (~> 1.1)
|
||||
letter_opener (~> 1.4)
|
||||
letter_opener_web (~> 1.3)
|
||||
link_header (~> 0.0)
|
||||
lograge (~> 0.5)
|
||||
microformats2 (~> 3.0)
|
||||
nokogiri (~> 1.7)
|
||||
oj (~> 3.0)
|
||||
lograge (~> 0.7)
|
||||
mario-redis-lock (~> 1.2)
|
||||
memory_profiler
|
||||
microformats (~> 4.0)
|
||||
mime-types (~> 3.1)
|
||||
net-ldap (~> 0.10)
|
||||
nokogiri (~> 1.8)
|
||||
nsa (~> 0.2)
|
||||
oj (~> 3.3)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-cas (~> 1.1)
|
||||
omniauth-saml (~> 1.10)
|
||||
ostatus2 (~> 2.0)
|
||||
ox (~> 2.5)
|
||||
ox (~> 2.8)
|
||||
paperclip (~> 5.1)
|
||||
paperclip-av-transcoder (~> 0.6)
|
||||
parallel_tests (~> 2.14)
|
||||
parallel_tests (~> 2.17)
|
||||
pg (~> 0.20)
|
||||
pghero (~> 1.7)
|
||||
pkg-config (~> 1.2)
|
||||
premailer-rails
|
||||
private_address_check (~> 0.4.1)
|
||||
pry-rails (~> 0.3)
|
||||
puma (~> 3.8)
|
||||
rabl (~> 0.13)
|
||||
puma (~> 3.10)
|
||||
pundit (~> 1.1)
|
||||
rack-attack (~> 5.0)
|
||||
rack-cors (~> 0.4)
|
||||
rack-timeout (~> 0.4)
|
||||
rails (~> 5.0)
|
||||
rails (~> 5.1.4)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 5.0)
|
||||
rails-settings-cached (~> 0.6)
|
||||
rdf-normalize (~> 0.3.1)
|
||||
redis (~> 3.3)
|
||||
redis-namespace (~> 1.5)
|
||||
redis-rails (~> 5.0)
|
||||
rqrcode (~> 0.10)
|
||||
rspec-rails (~> 3.6)
|
||||
rspec-rails (~> 3.7)
|
||||
rspec-sidekiq (~> 3.0)
|
||||
rubocop (~> 0.48)
|
||||
rubocop
|
||||
ruby-oembed (~> 0.12)
|
||||
ruby-progressbar (~> 1.4)
|
||||
sanitize (~> 4.4)
|
||||
scss_lint (~> 0.53)
|
||||
scss_lint (~> 0.55)
|
||||
sidekiq (~> 5.0)
|
||||
sidekiq-bulk (~> 0.1.1)
|
||||
sidekiq-scheduler (~> 2.1)
|
||||
sidekiq-unique-jobs (~> 5.0)
|
||||
simple-navigation (~> 4.0)
|
||||
simple_form (~> 3.4)
|
||||
simplecov (~> 0.14)
|
||||
sprockets-rails (~> 3.2)
|
||||
statsd-instrument (~> 2.1)
|
||||
streamio-ffmpeg (~> 3.0)
|
||||
strong_migrations
|
||||
tty-command
|
||||
tty-prompt
|
||||
twitter-text (~> 1.14)
|
||||
tzinfo-data (~> 1.2017)
|
||||
uglifier (~> 3.2)
|
||||
webmock (~> 3.0)
|
||||
webpacker (~> 1.2)
|
||||
webpacker (~> 3.0)
|
||||
webpush
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.4.1p111
|
||||
ruby 2.5.0p0
|
||||
|
||||
BUNDLED WITH
|
||||
1.14.6
|
||||
1.16.1
|
||||
|
@@ -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
|
||||
webpack: ./bin/webpack-dev-server --host 0.0.0.0
|
||||
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
|
||||
|
80
README.md
80
README.md
@@ -1,53 +1,70 @@
|
||||
Mastodon
|
||||

|
||||
========
|
||||
|
||||
[][travis]
|
||||
[][code_climate]
|
||||
[][travis]
|
||||
[][code_climate]
|
||||
|
||||
[travis]: https://travis-ci.org/tootsuite/mastodon
|
||||
[code_climate]: https://codeclimate.com/github/tootsuite/mastodon
|
||||
|
||||
Mastodon is a free, open-source social network server. A decentralized solution to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly.
|
||||
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.
|
||||
|
||||
An alternative implementation of the GNU social project. Based on [ActivityStreams](https://en.wikipedia.org/wiki/Activity_Streams_(format)), [Webfinger](https://en.wikipedia.org/wiki/WebFinger), [PubsubHubbub](https://en.wikipedia.org/wiki/PubSubHubbub) and [Salmon](https://en.wikipedia.org/wiki/Salmon_(protocol)).
|
||||
Click on the screenshot below to watch a demo of the UI:
|
||||
|
||||
Click on the screenshot to watch a demo of the UI:
|
||||
|
||||
[][youtube_demo]
|
||||
[][youtube_demo]
|
||||
|
||||
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
||||
|
||||
The project focus is a clean REST API and a good user interface. 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.
|
||||
**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.
|
||||
|
||||
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
|
||||
If you would like, you can [support the development of this project on Patreon][patreon] or [Liberapay][liberapay]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`
|
||||
|
||||
[patreon]: https://www.patreon.com/user?u=619786
|
||||
[liberapay]: https://liberapay.com/Mastodon/
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [List of Mastodon instances](https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/List-of-Mastodon-instances.md)
|
||||
- [Use this tool to find Twitter friends on Mastodon](https://mastodon-bridge.herokuapp.com)
|
||||
- [API overview](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md)
|
||||
- [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)
|
||||
|
||||
## Features
|
||||
|
||||
- **Fully interoperable with GNU social and any OStatus platform**
|
||||
Whatever implements Atom feeds, ActivityStreams, Salmon, PubSubHubbub and Webfinger is part of the network
|
||||
- **Real-time timeline updates**
|
||||
See the updates of people you're following appear in real-time in the UI via WebSockets
|
||||
- **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 WebM**
|
||||
Upload and view images and WebM videos attached to the updates
|
||||
- **OAuth2 and a straightforward REST API**
|
||||
Mastodon acts as an OAuth2 provider so 3rd party apps can use the API, which is RESTful and simple
|
||||
- **Background processing for long-running tasks**
|
||||
Mastodon tries to be as fast and responsive as possible, so all long-running tasks that can be delegated to background processing, are
|
||||
- **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
|
||||
|
||||
**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!
|
||||
|
||||
**Real-time 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!
|
||||
|
||||
**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.
|
||||
@@ -62,9 +79,8 @@ You can open issues for bugs you've found or features you think are missing. You
|
||||
|
||||
**IRC channel**: #mastodon on irc.freenode.net
|
||||
|
||||
---
|
||||
|
||||
## Extra credits
|
||||
|
||||
- The [Emoji One](https://github.com/Ranks/emojione) pack has been used for the emojis
|
||||
- The error page image courtesy of [Dopatwo](https://www.youtube.com/user/dopatwo)
|
||||
|
||||

|
||||
The elephant friend illustrations are created by [Dopatwo](https://mastodon.social/@dopatwo)
|
||||
|
10
Vagrantfile
vendored
10
Vagrantfile
vendored
@@ -35,16 +35,22 @@ sudo apt-get install \
|
||||
postgresql-contrib \
|
||||
protobuf-compiler \
|
||||
yarn \
|
||||
libicu-dev \
|
||||
libidn11-dev \
|
||||
libprotobuf-dev \
|
||||
libreadline-dev \
|
||||
libpam0g-dev \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
read RUBY_VERSION < .ruby-version
|
||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||
curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION
|
||||
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
|
||||
source /home/vagrant/.rvm/scripts/rvm
|
||||
|
||||
# Install Ruby
|
||||
rvm reinstall ruby-$RUBY_VERSION --disable-binary
|
||||
|
||||
# Configure database
|
||||
sudo -u postgres createuser -U postgres vagrant -s
|
||||
sudo -u postgres createdb -U postgres mastodon_development
|
||||
@@ -74,7 +80,7 @@ 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"
|
||||
|
2
app.json
2
app.json
@@ -2,7 +2,7 @@
|
||||
"name": "Mastodon",
|
||||
"description": "A GNU Social-compatible microblogging server",
|
||||
"repository": "https://github.com/tootsuite/mastodon",
|
||||
"logo": "https://github.com/tootsuite/mastodon/raw/master/app/assets/images/logo.png",
|
||||
"logo": "https://github.com/tootsuite.png",
|
||||
"env": {
|
||||
"HEROKU": {
|
||||
"description": "Leave this as true",
|
||||
|
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.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
|
@@ -2,9 +2,12 @@
|
||||
|
||||
class AboutController < ApplicationController
|
||||
before_action :set_body_classes
|
||||
before_action :set_instance_presenter, only: [:show, :more]
|
||||
before_action :set_instance_presenter, only: [:show, :more, :terms]
|
||||
|
||||
def show; end
|
||||
def show
|
||||
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
|
||||
@initial_state_json = serializable_resource.to_json
|
||||
end
|
||||
|
||||
def more; end
|
||||
|
||||
@@ -15,6 +18,7 @@ class AboutController < ApplicationController
|
||||
def new_user
|
||||
User.new.tap(&:build_account)
|
||||
end
|
||||
|
||||
helper_method :new_user
|
||||
|
||||
def set_instance_presenter
|
||||
@@ -24,4 +28,11 @@ class AboutController < ApplicationController
|
||||
def set_body_classes
|
||||
@body_classes = 'about-body'
|
||||
end
|
||||
|
||||
def initial_state_params
|
||||
{
|
||||
settings: { known_fediverse: Setting.show_known_fediverse_at_about_page },
|
||||
token: current_session&.token,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@@ -1,27 +1,111 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AccountsController < ApplicationController
|
||||
PAGE_SIZE = 20
|
||||
|
||||
include AccountControllerConcern
|
||||
|
||||
before_action :set_cache_headers
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@statuses = @account.statuses.permitted_for(@account, current_account).recent.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@pinned_statuses = []
|
||||
|
||||
if current_account && @account.blocking?(current_account)
|
||||
@statuses = []
|
||||
return
|
||||
end
|
||||
|
||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||
@statuses = filtered_status_page(params)
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
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.recent.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
@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.activitystreams2
|
||||
format.json do
|
||||
skip_session!
|
||||
|
||||
render_cached_json(['activitypub', 'actor', @account.cache_key], content_type: 'application/activity+json') do
|
||||
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def show_pinned_statuses?
|
||||
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_id].present?].none?
|
||||
end
|
||||
|
||||
def filtered_statuses
|
||||
default_statuses.tap do |statuses|
|
||||
statuses.merge!(only_media_scope) if media_requested?
|
||||
statuses.merge!(no_replies_scope) unless replies_requested?
|
||||
end
|
||||
end
|
||||
|
||||
def default_statuses
|
||||
@account.statuses.where(visibility: [:public, :unlisted])
|
||||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.where(id: account_media_status_ids)
|
||||
end
|
||||
|
||||
def account_media_status_ids
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
Status.without_replies
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:username])
|
||||
end
|
||||
|
||||
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: max_id, min_id: min_id)
|
||||
elsif replies_requested?
|
||||
short_account_with_replies_url(@account, max_id: max_id, min_id: min_id)
|
||||
else
|
||||
short_account_url(@account, max_id: max_id, min_id: min_id)
|
||||
end
|
||||
end
|
||||
|
||||
def media_requested?
|
||||
request.path.ends_with?('/media')
|
||||
end
|
||||
|
||||
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.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@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
|
41
app/controllers/activitypub/inboxes_controller.rb
Normal file
41
app/controllers/activitypub/inboxes_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::InboxesController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
if signed_request_account
|
||||
upgrade_account
|
||||
process_payload
|
||||
head 202
|
||||
else
|
||||
render plain: signature_verification_failure_reason, status: 401
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username]) if params[:account_username]
|
||||
end
|
||||
|
||||
def body
|
||||
@body ||= request.body.read
|
||||
end
|
||||
|
||||
def upgrade_account
|
||||
if signed_request_account.ostatus?
|
||||
signed_request_account.update(last_webfingered_at: nil)
|
||||
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
||||
end
|
||||
|
||||
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
|
||||
DeliveryFailureTracker.track_inverse_success!(signed_request_account)
|
||||
end
|
||||
|
||||
def process_payload
|
||||
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body.force_encoding('UTF-8'))
|
||||
end
|
||||
end
|
29
app/controllers/activitypub/outboxes_controller.rb
Normal file
29
app/controllers/activitypub/outboxes_controller.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::OutboxesController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
|
||||
def show
|
||||
@statuses = @account.statuses.permitted_for(@account, signed_request_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
|
||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
||||
def outbox_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_outbox_url(@account),
|
||||
type: :ordered,
|
||||
size: @account.statuses_count,
|
||||
items: @statuses
|
||||
)
|
||||
end
|
||||
end
|
41
app/controllers/admin/account_moderation_notes_controller.rb
Normal file
41
app/controllers/admin/account_moderation_notes_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
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
|
||||
end
|
@@ -2,16 +2,78 @@
|
||||
|
||||
module Admin
|
||||
class AccountsController < BaseController
|
||||
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :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
|
||||
@account = Account.find(params[:id])
|
||||
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!
|
||||
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def require_remote_account!
|
||||
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
|
||||
@@ -27,7 +89,8 @@ module Admin
|
||||
: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
|
||||
|
@@ -2,15 +2,19 @@
|
||||
|
||||
module Admin
|
||||
class ConfirmationsController < BaseController
|
||||
before_action :set_user
|
||||
|
||||
def create
|
||||
account_user.confirm
|
||||
authorize @user, :confirm?
|
||||
@user.confirm!
|
||||
log_action :confirm, @user
|
||||
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
|
||||
end
|
||||
end
|
||||
|
111
app/controllers/admin/custom_emojis_controller.rb
Normal file
111
app/controllers/admin/custom_emojis_controller.rb
Normal file
@@ -0,0 +1,111 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class CustomEmojisController < BaseController
|
||||
before_action :set_custom_emoji, except: [:index, :new, :create]
|
||||
before_action :set_filter_params
|
||||
|
||||
def index
|
||||
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
|
||||
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
|
||||
authorize @custom_emoji, :copy?
|
||||
|
||||
emoji = CustomEmoji.find_or_initialize_by(domain: nil,
|
||||
shortcode: @custom_emoji.shortcode)
|
||||
emoji.image = @custom_emoji.image
|
||||
|
||||
if emoji.save
|
||||
log_action :create, emoji
|
||||
flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
|
||||
else
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
def set_custom_emoji
|
||||
@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, :visible_in_picker)
|
||||
end
|
||||
|
||||
def filtered_custom_emojis
|
||||
CustomEmojiFilter.new(filter_params).results
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.permit(
|
||||
:local,
|
||||
:remote,
|
||||
:by_domain,
|
||||
:shortcode
|
||||
)
|
||||
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
|
||||
|
||||
|
47
app/controllers/admin/email_domain_blocks_controller.rb
Normal file
47
app/controllers/admin/email_domain_blocks_controller.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class EmailDomainBlocksController < BaseController
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def 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
|
||||
|
||||
private
|
||||
|
||||
def set_email_domain_block
|
||||
@email_domain_block = EmailDomainBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:email_domain_block).permit(:domain)
|
||||
end
|
||||
end
|
||||
end
|
@@ -3,13 +3,41 @@
|
||||
module Admin
|
||||
class InstancesController < BaseController
|
||||
def index
|
||||
@instances = ordered_instances.page(params[:page])
|
||||
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
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filtered_instances
|
||||
InstanceFilter.new(filter_params).results
|
||||
end
|
||||
|
||||
def paginated_instances
|
||||
filtered_instances.page(params[:page])
|
||||
end
|
||||
|
||||
helper_method :paginated_instances
|
||||
|
||||
def ordered_instances
|
||||
Account.remote.by_domain_accounts
|
||||
paginated_instances.map { |account| Instance.new(account) }
|
||||
end
|
||||
|
||||
def subscribeable_accounts
|
||||
Account.with_followers.remote.where(domain: params[:by_domain])
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.permit(
|
||||
:domain_name
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
47
app/controllers/admin/invites_controller.rb
Normal file
47
app/controllers/admin/invites_controller.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
# 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
|
||||
|
||||
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
|
@@ -1,9 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class PubsubhubbubController < BaseController
|
||||
def index
|
||||
@subscriptions = Subscription.order(id: :desc).includes(:account).page(params[:page])
|
||||
end
|
||||
end
|
||||
end
|
@@ -3,16 +3,29 @@
|
||||
module Admin
|
||||
class ReportedStatusesController < BaseController
|
||||
before_action :set_report
|
||||
before_action :set_status
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
def create
|
||||
authorize :status, :update?
|
||||
|
||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account))
|
||||
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)
|
||||
authorize @status, :update?
|
||||
@status.update!(status_params)
|
||||
log_action :update, @status
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
redirect_to admin_report_path(@report)
|
||||
log_action :destroy, @status
|
||||
render json: @status
|
||||
end
|
||||
|
||||
private
|
||||
@@ -21,6 +34,10 @@ module Admin
|
||||
params.require(:status).permit(:sensitive)
|
||||
end
|
||||
|
||||
def form_status_batch_params
|
||||
params.require(:form_status_batch).permit(:action, status_ids: [])
|
||||
end
|
||||
|
||||
def set_report
|
||||
@report = Report.find(params[:report_id])
|
||||
end
|
||||
|
@@ -5,12 +5,17 @@ module Admin
|
||||
before_action :set_report, except: [:index]
|
||||
|
||||
def index
|
||||
authorize :report, :index?
|
||||
@reports = filtered_reports.page(params[:page])
|
||||
end
|
||||
|
||||
def show; end
|
||||
def show
|
||||
authorize @report, :show?
|
||||
@form = Form::StatusBatch.new
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @report, :update?
|
||||
process_report
|
||||
redirect_to admin_report_path(@report)
|
||||
end
|
||||
@@ -20,12 +25,17 @@ module Admin
|
||||
def process_report
|
||||
case params[:outcome].to_s
|
||||
when 'resolve'
|
||||
@report.update(action_taken_by_current_attributes)
|
||||
@report.update!(action_taken_by_current_attributes)
|
||||
log_action :resolve, @report
|
||||
when 'suspend'
|
||||
Admin::SuspensionWorker.perform_async(@report.target_account.id)
|
||||
log_action :resolve, @report
|
||||
log_action :suspend, @report.target_account
|
||||
resolve_all_target_account_reports
|
||||
when 'silence'
|
||||
@report.target_account.update(silenced: true)
|
||||
@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
|
||||
|
@@ -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
|
@@ -8,34 +8,67 @@ module Admin
|
||||
site_title
|
||||
site_description
|
||||
site_extended_description
|
||||
site_terms
|
||||
open_registrations
|
||||
closed_registrations_message
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
bootstrap_timeline_accounts
|
||||
thumbnail
|
||||
hero
|
||||
min_invite_role
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
).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
|
||||
).freeze
|
||||
|
||||
UPLOAD_SETTINGS = %w(
|
||||
thumbnail
|
||||
hero
|
||||
).freeze
|
||||
BOOLEAN_SETTINGS = %w(open_registrations).freeze
|
||||
|
||||
def edit
|
||||
@settings = Setting.all_as_records
|
||||
authorize :settings, :show?
|
||||
@admin_settings = Form::AdminSettings.new
|
||||
end
|
||||
|
||||
def update
|
||||
authorize :settings, :update?
|
||||
|
||||
settings_params.each do |key, value|
|
||||
setting = Setting.where(var: key).first_or_initialize(var: key)
|
||||
setting.update(value: value_for_update(key, value))
|
||||
if UPLOAD_SETTINGS.include?(key)
|
||||
upload = SiteUpload.where(var: key).first_or_initialize(var: key)
|
||||
upload.update(file: value)
|
||||
else
|
||||
setting = Setting.where(var: key).first_or_initialize(var: key)
|
||||
setting.update(value: value_for_update(key, value))
|
||||
end
|
||||
end
|
||||
|
||||
flash[:notice] = 'Success!'
|
||||
flash[:notice] = I18n.t('generic.changes_saved_msg')
|
||||
redirect_to edit_admin_settings_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def settings_params
|
||||
params.permit(ADMIN_SETTINGS)
|
||||
params.require(:form_admin_settings).permit(ADMIN_SETTINGS)
|
||||
end
|
||||
|
||||
def value_for_update(key, value)
|
||||
if BOOLEAN_SETTINGS.include?(key)
|
||||
value == 'true'
|
||||
value == '1'
|
||||
else
|
||||
value
|
||||
end
|
||||
|
@@ -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
|
||||
|
||||
|
76
app/controllers/admin/statuses_controller.rb
Normal file
76
app/controllers/admin/statuses_controller.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class StatusesController < BaseController
|
||||
helper_method :current_params
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_status, only: [:update, :destroy]
|
||||
|
||||
PER_PAGE = 20
|
||||
|
||||
def index
|
||||
authorize :status, :index?
|
||||
|
||||
@statuses = @account.statuses
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :status, :update?
|
||||
|
||||
@form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account))
|
||||
flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
|
||||
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @status, :update?
|
||||
@status.update!(status_params)
|
||||
log_action :update, @status
|
||||
redirect_to admin_account_statuses_path(@account.id, current_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @status, :destroy?
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
log_action :destroy, @status
|
||||
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
|
||||
end
|
||||
end
|
20
app/controllers/admin/subscriptions_controller.rb
Normal file
20
app/controllers/admin/subscriptions_controller.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class SubscriptionsController < BaseController
|
||||
def index
|
||||
authorize :subscription, :index?
|
||||
@subscriptions = ordered_subscriptions.page(requested_page)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ordered_subscriptions
|
||||
Subscription.order(id: :desc).includes(:account)
|
||||
end
|
||||
|
||||
def requested_page
|
||||
params[:page].to_i
|
||||
end
|
||||
end
|
||||
end
|
@@ -5,12 +5,16 @@ module Admin
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
authorize @account, :suspend?
|
||||
Admin::SuspensionWorker.perform_async(@account.id)
|
||||
log_action :suspend, @account
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account.update(suspended: false)
|
||||
authorize @account, :unsuspend?
|
||||
@account.unsuspend!
|
||||
log_action :unsuspend, @account
|
||||
redirect_to admin_accounts_path
|
||||
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
|
||||
|
||||
|
@@ -1,25 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Activitypub::ActivitiesController < ApiController
|
||||
# before_action :set_follow, only: [:show_follow]
|
||||
before_action :set_status, only: [:show_status]
|
||||
|
||||
respond_to :activitystreams2
|
||||
|
||||
# Show a status in AS2 format, as either an Announce (reblog) or a Create (post) activity.
|
||||
def show_status
|
||||
return forbidden unless @status.permitted?
|
||||
|
||||
if @status.reblog?
|
||||
render :show_status_announce
|
||||
else
|
||||
render :show_status_create
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
end
|
||||
end
|
@@ -1,17 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Activitypub::NotesController < ApiController
|
||||
before_action :set_status
|
||||
|
||||
respond_to :activitystreams2
|
||||
|
||||
def show
|
||||
forbidden unless @status.permitted?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
end
|
||||
end
|
@@ -1,69 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::Activitypub::OutboxController < ApiController
|
||||
before_action :set_account
|
||||
|
||||
respond_to :activitystreams2
|
||||
|
||||
def show
|
||||
if params[:max_id] || params[:since_id]
|
||||
show_outbox_page
|
||||
else
|
||||
show_base_outbox
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def show_base_outbox
|
||||
@statuses = Status.as_outbox_timeline(@account)
|
||||
@statuses = cache_collection(@statuses)
|
||||
|
||||
set_maps(@statuses)
|
||||
|
||||
set_first_last_page(@statuses)
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
def show_outbox_page
|
||||
all_statuses = Status.as_outbox_timeline(@account)
|
||||
@statuses = all_statuses.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
|
||||
all_statuses = cache_collection(all_statuses)
|
||||
@statuses = cache_collection(@statuses)
|
||||
|
||||
set_maps(@statuses)
|
||||
|
||||
set_first_last_page(all_statuses)
|
||||
|
||||
@next_page_url = api_activitypub_outbox_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
|
||||
@prev_page_url = api_activitypub_outbox_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
|
||||
|
||||
@paginated = @next_page_url || @prev_page_url
|
||||
@part_of_url = api_activitypub_outbox_url
|
||||
|
||||
set_pagination_headers(@next_page_url, @prev_page_url)
|
||||
|
||||
render :show_page
|
||||
end
|
||||
|
||||
def cache_collection(raw)
|
||||
super(raw, Status)
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def set_first_last_page(statuses) # rubocop:disable Style/AccessorMethodName
|
||||
return if statuses.empty?
|
||||
|
||||
@first_page_url = api_activitypub_outbox_url(max_id: statuses.first.id + 1)
|
||||
@last_page_url = api_activitypub_outbox_url(since_id: statuses.last.id - 1)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
end
|
79
app/controllers/api/base_controller.rb
Normal file
79
app/controllers/api/base_controller.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::BaseController < ApplicationController
|
||||
DEFAULT_STATUSES_LIMIT = 20
|
||||
DEFAULT_ACCOUNTS_LIMIT = 40
|
||||
|
||||
include RateLimitHeaders
|
||||
|
||||
skip_before_action :store_current_location
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
end
|
||||
|
||||
rescue_from ActiveRecord::RecordNotFound do
|
||||
render json: { error: 'Record not found' }, status: 404
|
||||
end
|
||||
|
||||
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
|
||||
render json: { error: 'Remote data could not be fetched' }, status: 503
|
||||
end
|
||||
|
||||
rescue_from OpenSSL::SSL::SSLError do
|
||||
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
|
||||
end
|
||||
|
||||
rescue_from Mastodon::NotPermittedError do
|
||||
render json: { error: 'This action is not allowed' }, status: 403
|
||||
end
|
||||
|
||||
def doorkeeper_unauthorized_render_options(error: nil)
|
||||
{ json: { error: (error.try(:description) || 'Not authorized') } }
|
||||
end
|
||||
|
||||
def doorkeeper_forbidden_render_options(*)
|
||||
{ json: { error: 'This action is outside the authorized scopes' } }
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_pagination_headers(next_path = nil, prev_path = nil)
|
||||
links = []
|
||||
links << [next_path, [%w(rel next)]] if next_path
|
||||
links << [prev_path, [%w(rel prev)]] if prev_path
|
||||
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
|
||||
end
|
||||
|
||||
def limit_param(default_limit)
|
||||
return default_limit unless params[:limit]
|
||||
[params[:limit].to_i.abs, default_limit * 2].min
|
||||
end
|
||||
|
||||
def truthy_param?(key)
|
||||
ActiveModel::Type::Boolean.new.cast(params[key])
|
||||
end
|
||||
|
||||
def current_resource_owner
|
||||
@current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
||||
end
|
||||
|
||||
def current_user
|
||||
current_resource_owner || super
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
nil
|
||||
end
|
||||
|
||||
def require_user!
|
||||
if current_user
|
||||
set_user_activity
|
||||
else
|
||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||
end
|
||||
end
|
||||
|
||||
def render_empty
|
||||
render json: {}, status: 200
|
||||
end
|
||||
end
|
@@ -1,33 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::OEmbedController < ApiController
|
||||
class Api::OEmbedController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@stream_entry = stream_entry_from_url(params[:url])
|
||||
@width = params[:maxwidth].present? ? params[:maxwidth].to_i : 400
|
||||
@height = params[:maxheight].present? ? params[:maxheight].to_i : nil
|
||||
@status = status_finder.status
|
||||
render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stream_entry_from_url(url)
|
||||
params = Rails.application.routes.recognize_path(url)
|
||||
|
||||
raise ActiveRecord::RecordNotFound unless recognized_stream_entry_url?(params)
|
||||
|
||||
stream_entry(params)
|
||||
def status_finder
|
||||
StatusFinder.new(params[:url])
|
||||
end
|
||||
|
||||
def recognized_stream_entry_url?(params)
|
||||
%w(stream_entries statuses).include?(params[:controller]) && params[:action] == 'show'
|
||||
def maxwidth_or_default
|
||||
(params[:maxwidth].presence || 400).to_i
|
||||
end
|
||||
|
||||
def stream_entry(params)
|
||||
if params[:controller] == 'stream_entries'
|
||||
StreamEntry.find(params[:id])
|
||||
else
|
||||
Status.find(params[:id]).stream_entry
|
||||
end
|
||||
def maxheight_or_default
|
||||
params[:maxheight].present? ? params[:maxheight].to_i : nil
|
||||
end
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::PushController < ApiController
|
||||
class Api::PushController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
def update
|
||||
response, status = process_push_request
|
||||
render plain: response, status: status
|
||||
@@ -11,7 +13,7 @@ class Api::PushController < ApiController
|
||||
def process_push_request
|
||||
case hub_mode
|
||||
when 'subscribe'
|
||||
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds)
|
||||
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds, verified_domain)
|
||||
when 'unsubscribe'
|
||||
Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback)
|
||||
else
|
||||
@@ -57,6 +59,10 @@ class Api::PushController < ApiController
|
||||
TagManager.instance.web_domain?(hub_topic_domain)
|
||||
end
|
||||
|
||||
def verified_domain
|
||||
return signed_request_account.domain if signed_request_account
|
||||
end
|
||||
|
||||
def hub_topic_domain
|
||||
hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '')
|
||||
end
|
||||
|
@@ -1,17 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::SalmonController < ApiController
|
||||
class Api::SalmonController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
respond_to :txt
|
||||
|
||||
def update
|
||||
payload = request.body.read
|
||||
|
||||
if !payload.nil? && verify?(payload)
|
||||
SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8'))
|
||||
head 201
|
||||
else
|
||||
if verify_payload?
|
||||
process_salmon
|
||||
head 202
|
||||
elsif payload.present?
|
||||
render plain: signature_verification_failure_reason, status: 401
|
||||
else
|
||||
head 400
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +23,15 @@ class Api::SalmonController < ApiController
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def verify?(payload)
|
||||
VerifySalmonService.new.call(payload)
|
||||
def payload
|
||||
@_payload ||= request.body.read
|
||||
end
|
||||
|
||||
def verify_payload?
|
||||
payload.present? && VerifySalmonService.new.call(payload)
|
||||
end
|
||||
|
||||
def process_salmon
|
||||
SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8'))
|
||||
end
|
||||
end
|
||||
|
@@ -1,22 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::SubscriptionsController < ApiController
|
||||
class Api::SubscriptionsController < Api::BaseController
|
||||
before_action :set_account
|
||||
respond_to :txt
|
||||
|
||||
def show
|
||||
if @account.subscription(api_subscription_url(@account.id)).valid?(params['hub.topic'])
|
||||
@account.update(subscription_expires_at: Time.now.utc + (params['hub.lease_seconds'] || 86_400).to_i.seconds)
|
||||
render plain: HTMLEntities.new.encode(params['hub.challenge']), status: 200
|
||||
if subscription.valid?(params['hub.topic'])
|
||||
@account.update(subscription_expires_at: future_expires)
|
||||
render plain: encoded_challenge, status: 200
|
||||
else
|
||||
head 404
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
body = request.body.read
|
||||
subscription = @account.subscription(api_subscription_url(@account.id))
|
||||
|
||||
if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE'])
|
||||
ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8'))
|
||||
end
|
||||
@@ -26,6 +23,28 @@ class Api::SubscriptionsController < ApiController
|
||||
|
||||
private
|
||||
|
||||
def subscription
|
||||
@_subscription ||= @account.subscription(
|
||||
api_subscription_url(@account.id)
|
||||
)
|
||||
end
|
||||
|
||||
def body
|
||||
@_body ||= request.body.read
|
||||
end
|
||||
|
||||
def encoded_challenge
|
||||
HTMLEntities.new.encode(params['hub.challenge'])
|
||||
end
|
||||
|
||||
def future_expires
|
||||
Time.now.utc + lease_seconds_or_default
|
||||
end
|
||||
|
||||
def lease_seconds_or_default
|
||||
(params['hub.lease_seconds'] || 1.day).to_i.seconds
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
25
app/controllers/api/v1/accounts/credentials_controller.rb
Normal file
25
app/controllers/api/v1/accounts/credentials_controller.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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 :require_user!
|
||||
|
||||
def show
|
||||
@account = current_account
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@account = current_account
|
||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.permit(:display_name, :note, :avatar, :header, :locked)
|
||||
end
|
||||
end
|
@@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:active_relationships).references(:active_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
Follow.where(target_account: @account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_followers_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_account_followers_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.active_relationships.first.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.active_relationships.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
end
|
@@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_follows).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:passive_relationships).references(:passive_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
Follow.where(account: @account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_following_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_account_following_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.passive_relationships.first.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.passive_relationships.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.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 }
|
||||
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
|
26
app/controllers/api/v1/accounts/relationships_controller.rb
Normal file
26
app/controllers/api/v1/accounts/relationships_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
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).compact
|
||||
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relationships
|
||||
AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
|
||||
end
|
||||
|
||||
def account_ids
|
||||
Array(params[:id]).map(&:to_i)
|
||||
end
|
||||
end
|
25
app/controllers/api/v1/accounts/search_controller.rb
Normal file
25
app/controllers/api/v1/accounts/search_controller.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::SearchController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@accounts = account_search
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_search
|
||||
AccountSearchService.new.call(
|
||||
params[:q],
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
current_account,
|
||||
resolve: truthy_param?(:resolve),
|
||||
following: truthy_param?(:following)
|
||||
)
|
||||
end
|
||||
end
|
96
app/controllers/api/v1/accounts/statuses_controller.rb
Normal file
96
app/controllers/api/v1/accounts/statuses_controller.rb
Normal file
@@ -0,0 +1,96 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def load_statuses
|
||||
cached_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
cache_collection account_statuses, Status
|
||||
end
|
||||
|
||||
def account_statuses
|
||||
default_statuses.tap do |statuses|
|
||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||
statuses.merge!(pinned_scope) if truthy_param?(:pinned)
|
||||
statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
|
||||
end
|
||||
end
|
||||
|
||||
def default_statuses
|
||||
permitted_account_statuses.paginate_by_max_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def permitted_account_statuses
|
||||
@account.statuses.permitted_for(@account, current_account)
|
||||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.where(id: account_media_status_ids)
|
||||
end
|
||||
|
||||
def account_media_status_ids
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
end
|
||||
|
||||
def pinned_scope
|
||||
@account.pinned_statuses
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
Status.without_replies
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_account_statuses_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @statuses.empty?
|
||||
api_v1_account_statuses_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
end
|
@@ -1,132 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountsController < ApiController
|
||||
before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute, :update_credentials]
|
||||
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 -> { doorkeeper_authorize! :write }, only: [:update_credentials]
|
||||
before_action :require_user!, except: [:show, :following, :followers, :statuses]
|
||||
before_action :set_account, except: [:verify_credentials, :update_credentials, :suggestions, :search]
|
||||
before_action :require_user!, except: [:show]
|
||||
before_action :set_account
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show; end
|
||||
|
||||
def verify_credentials
|
||||
@account = current_user.account
|
||||
render :show
|
||||
end
|
||||
|
||||
def update_credentials
|
||||
current_account.update!(account_params)
|
||||
@account = current_account
|
||||
render :show
|
||||
end
|
||||
|
||||
def following
|
||||
@accounts = Account.includes(:passive_relationships)
|
||||
.references(:passive_relationships)
|
||||
.merge(Follow.where(account: @account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = following_api_v1_account_url(pagination_params(max_id: @accounts.last.passive_relationships.first.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = following_api_v1_account_url(pagination_params(since_id: @accounts.first.passive_relationships.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
render :index
|
||||
end
|
||||
|
||||
def followers
|
||||
@accounts = Account.includes(:active_relationships)
|
||||
.references(:active_relationships)
|
||||
.merge(Follow.where(target_account: @account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = followers_api_v1_account_url(pagination_params(max_id: @accounts.last.active_relationships.first.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = followers_api_v1_account_url(pagination_params(since_id: @accounts.first.active_relationships.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
render :index
|
||||
end
|
||||
|
||||
def statuses
|
||||
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
@statuses = @statuses.where(id: MediaAttachment.where(account: @account).where.not(status_id: nil).reorder('').select('distinct status_id')) if params[:only_media]
|
||||
@statuses = @statuses.without_replies if params[:exclude_replies]
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
|
||||
set_maps(@statuses)
|
||||
|
||||
next_path = statuses_api_v1_account_url(statuses_pagination_params(max_id: @statuses.last.id)) if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
prev_path = statuses_api_v1_account_url(statuses_pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
def show
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account.acct)
|
||||
set_relationship
|
||||
render :relationship
|
||||
FollowService.new.call(current_user.account, @account.acct, reblogs: truthy_param?(:reblogs))
|
||||
|
||||
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
|
||||
|
||||
def block
|
||||
BlockService.new.call(current_user.account, @account)
|
||||
|
||||
@following = { @account.id => false }
|
||||
@followed_by = { @account.id => false }
|
||||
@blocking = { @account.id => true }
|
||||
@requested = { @account.id => false }
|
||||
@muting = { @account.id => current_account.muting?(@account.id) }
|
||||
@domain_blocking = { @account.id => current_account.domain_blocking?(@account.domain) }
|
||||
|
||||
render :relationship
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def mute
|
||||
MuteService.new.call(current_user.account, @account)
|
||||
set_relationship
|
||||
render :relationship
|
||||
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unfollow
|
||||
UnfollowService.new.call(current_user.account, @account)
|
||||
set_relationship
|
||||
render :relationship
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unblock
|
||||
UnblockService.new.call(current_user.account, @account)
|
||||
set_relationship
|
||||
render :relationship
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
def unmute
|
||||
UnmuteService.new.call(current_user.account, @account)
|
||||
set_relationship
|
||||
render :relationship
|
||||
end
|
||||
|
||||
def relationships
|
||||
ids = params[:id].is_a?(Enumerable) ? params[:id].map(&:to_i) : [params[:id].to_i]
|
||||
|
||||
@accounts = Account.where(id: ids).select('id')
|
||||
@following = Account.following_map(ids, current_user.account_id)
|
||||
@followed_by = Account.followed_by_map(ids, current_user.account_id)
|
||||
@blocking = Account.blocking_map(ids, current_user.account_id)
|
||||
@muting = Account.muting_map(ids, current_user.account_id)
|
||||
@requested = Account.requested_map(ids, current_user.account_id)
|
||||
@domain_blocking = Account.domain_blocking_map(ids, current_user.account_id)
|
||||
end
|
||||
|
||||
def search
|
||||
@accounts = AccountSearchService.new.call(params[:q], limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:resolve] == 'true', current_account)
|
||||
|
||||
render :index
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
private
|
||||
@@ -135,24 +51,7 @@ class Api::V1::AccountsController < ApiController
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def set_relationship
|
||||
@following = Account.following_map([@account.id], current_user.account_id)
|
||||
@followed_by = Account.followed_by_map([@account.id], current_user.account_id)
|
||||
@blocking = Account.blocking_map([@account.id], current_user.account_id)
|
||||
@muting = Account.muting_map([@account.id], current_user.account_id)
|
||||
@requested = Account.requested_map([@account.id], current_user.account_id)
|
||||
@domain_blocking = Account.domain_blocking_map([@account.id], current_user.account_id)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def statuses_pagination_params(core_params)
|
||||
params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:display_name, :note, :avatar, :header)
|
||||
def relationships(**options)
|
||||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
|
||||
end
|
||||
end
|
||||
|
11
app/controllers/api/v1/apps/credentials_controller.rb
Normal file
11
app/controllers/api/v1/apps/credentials_controller.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Apps::CredentialsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
render json: doorkeeper_token.application, serializer: REST::StatusSerializer::ApplicationSerializer
|
||||
end
|
||||
end
|
@@ -1,14 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AppsController < ApiController
|
||||
respond_to :json
|
||||
|
||||
class Api::V1::AppsController < Api::BaseController
|
||||
def create
|
||||
@app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
|
||||
@app = Doorkeeper::Application.create!(application_options)
|
||||
render json: @app, serializer: REST::ApplicationSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def application_options
|
||||
{
|
||||
name: app_params[:client_name],
|
||||
redirect_uri: app_params[:redirect_uris],
|
||||
scopes: app_scopes_or_default,
|
||||
website: app_params[:website],
|
||||
}
|
||||
end
|
||||
|
||||
def app_scopes_or_default
|
||||
app_params[:scopes] || Doorkeeper.configuration.default_scopes
|
||||
end
|
||||
|
||||
def app_params
|
||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
||||
end
|
||||
|
@@ -1,26 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::BlocksController < ApiController
|
||||
class Api::V1::BlocksController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = Account.includes(:blocked_by)
|
||||
.references(:blocked_by)
|
||||
.merge(Block.where(account: current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = api_v1_blocks_url(pagination_params(max_id: @accounts.last.blocked_by_ids.last)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = api_v1_blocks_url(pagination_params(since_id: @accounts.first.blocked_by_ids.first)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
paginated_blocks.map(&:target_account)
|
||||
end
|
||||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.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
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_blocks_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless paginated_blocks.empty?
|
||||
api_v1_blocks_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
paginated_blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
paginated_blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
9
app/controllers/api/v1/custom_emojis_controller.rb
Normal file
9
app/controllers/api/v1/custom_emojis_controller.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::CustomEmojisController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer
|
||||
end
|
||||
end
|
@@ -1,18 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::DomainBlocksController < ApiController
|
||||
class Api::V1::DomainBlocksController < Api::BaseController
|
||||
BLOCK_LIMIT = 100
|
||||
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :show
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@blocks = AccountDomainBlock.where(account: current_account).paginate_by_max_id(limit_param(100), params[:max_id], params[:since_id])
|
||||
|
||||
next_path = api_v1_domain_blocks_url(pagination_params(max_id: @blocks.last.id)) if @blocks.size == limit_param(100)
|
||||
prev_path = api_v1_domain_blocks_url(pagination_params(since_id: @blocks.first.id)) unless @blocks.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@blocks = load_domain_blocks
|
||||
render json: @blocks.map(&:domain)
|
||||
end
|
||||
|
||||
@@ -28,6 +26,46 @@ class Api::V1::DomainBlocksController < ApiController
|
||||
|
||||
private
|
||||
|
||||
def load_domain_blocks
|
||||
account_domain_blocks.paginate_by_max_id(
|
||||
limit_param(BLOCK_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def account_domain_blocks
|
||||
current_account.domain_blocks
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_domain_blocks_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @blocks.empty?
|
||||
api_v1_domain_blocks_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@blocks.size == limit_param(BLOCK_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
@@ -1,25 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FavouritesController < ApiController
|
||||
class Api::V1::FavouritesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
results = Favourite.where(account: current_account).paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(Status.where(id: results.map(&:status_id)), Status)
|
||||
|
||||
set_maps(@statuses)
|
||||
|
||||
next_path = api_v1_favourites_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
prev_path = api_v1_favourites_url(pagination_params(since_id: results.first.id)) unless results.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@statuses = load_statuses
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_statuses
|
||||
cached_favourites
|
||||
end
|
||||
|
||||
def cached_favourites
|
||||
cache_collection(
|
||||
Status.reorder(nil).joins(:favourites).merge(results),
|
||||
Status
|
||||
)
|
||||
end
|
||||
|
||||
def results
|
||||
@_results ||= account_favourites.paginate_by_max_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def account_favourites
|
||||
current_account.favourites
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_favourites_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless results.empty?
|
||||
api_v1_favourites_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
results.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
results.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
@@ -1,34 +1,75 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowRequestsController < ApiController
|
||||
class Api::V1::FollowRequestsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
def index
|
||||
@accounts = Account.includes(:follow_requests)
|
||||
.references(:follow_requests)
|
||||
.merge(FollowRequest.where(target_account: current_account)
|
||||
.paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = api_v1_follow_requests_url(pagination_params(max_id: @accounts.last.follow_requests.last.id)) if @accounts.size == DEFAULT_ACCOUNTS_LIMIT
|
||||
prev_path = api_v1_follow_requests_url(pagination_params(since_id: @accounts.first.follow_requests.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
def authorize
|
||||
AuthorizeFollowService.new.call(Account.find(params[:id]), current_account)
|
||||
AuthorizeFollowService.new.call(account, current_account)
|
||||
render_empty
|
||||
end
|
||||
|
||||
def reject
|
||||
RejectFollowService.new.call(Account.find(params[:id]), current_account)
|
||||
RejectFollowService.new.call(account, current_account)
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account
|
||||
Account.find(params[:id])
|
||||
end
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_follow_requests).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:follow_requests).references(:follow_requests)
|
||||
end
|
||||
|
||||
def paginated_follow_requests
|
||||
FollowRequest.where(target_account: current_account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_follow_requests_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_follow_requests_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.follow_requests.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.follow_requests.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::FollowsController < ApiController
|
||||
class Api::V1::FollowsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
|
||||
@@ -10,7 +10,13 @@ class Api::V1::FollowsController < ApiController
|
||||
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
|
||||
|
||||
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
|
||||
render :show
|
||||
|
||||
if @account.nil?
|
||||
username, domain = target_uri.split('@')
|
||||
@account = Account.find_remote!(username, domain)
|
||||
end
|
||||
|
||||
render json: @account, serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
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
|
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::InstancesController < ApiController
|
||||
class Api::V1::InstancesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def show; end
|
||||
def show
|
||||
render json: {}, serializer: REST::InstanceSerializer
|
||||
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 }, only: [:show]
|
||||
before_action -> { doorkeeper_authorize! :write }, 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.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 }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write }, 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,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MediaController < ApiController
|
||||
class Api::V1::MediaController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action :require_user!
|
||||
|
||||
@@ -10,16 +10,31 @@ class Api::V1::MediaController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
|
||||
@media = current_account.media_attachments.create!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
|
||||
render json: { error: 'File type of uploaded media could not be verified' }, status: 422
|
||||
render json: file_type_error, status: 422
|
||||
rescue Paperclip::Error
|
||||
render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
|
||||
render json: processing_error, status: 500
|
||||
end
|
||||
|
||||
def update
|
||||
@media = current_account.media_attachments.where(status_id: nil).find(params[:id])
|
||||
@media.update!(media_params)
|
||||
render json: @media, serializer: REST::MediaAttachmentSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def media_params
|
||||
params.permit(:file)
|
||||
params.permit(:file, :description, :focus)
|
||||
end
|
||||
|
||||
def file_type_error
|
||||
{ error: 'File type of uploaded media could not be verified' }
|
||||
end
|
||||
|
||||
def processing_error
|
||||
{ error: 'Error processing thumbnail for uploaded media' }
|
||||
end
|
||||
end
|
||||
|
@@ -1,26 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::MutesController < ApiController
|
||||
class Api::V1::MutesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@accounts = Account.includes(:muted_by)
|
||||
.references(:muted_by)
|
||||
.merge(Mute.where(account: current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = api_v1_mutes_url(pagination_params(max_id: @accounts.last.muted_by_ids.last)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = api_v1_mutes_url(pagination_params(since_id: @accounts.first.muted_by_ids.first)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_accounts
|
||||
default_accounts.merge(paginated_mutes).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:muted_by).references(:muted_by)
|
||||
end
|
||||
|
||||
def paginated_mutes
|
||||
Mute.where(account: current_account).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_mutes_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_mutes_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.muted_by_ids.last
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.muted_by_ids.first
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
@@ -1,42 +1,80 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::NotificationsController < ApiController
|
||||
class Api::V1::NotificationsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
respond_to :json
|
||||
|
||||
DEFAULT_NOTIFICATIONS_LIMIT = 15
|
||||
|
||||
def index
|
||||
@notifications = Notification.where(account: current_account).browserable(exclude_types).paginate_by_max_id(limit_param(DEFAULT_NOTIFICATIONS_LIMIT), params[:max_id], params[:since_id])
|
||||
@notifications = cache_collection(@notifications, Notification)
|
||||
statuses = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
|
||||
|
||||
set_maps(statuses)
|
||||
|
||||
next_path = api_v1_notifications_url(pagination_params(max_id: @notifications.last.id)) unless @notifications.empty?
|
||||
prev_path = api_v1_notifications_url(pagination_params(since_id: @notifications.first.id)) unless @notifications.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
@notifications = load_notifications
|
||||
render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||
end
|
||||
|
||||
def show
|
||||
@notification = Notification.where(account: current_account).find(params[:id])
|
||||
@notification = current_account.notifications.find(params[:id])
|
||||
render json: @notification, serializer: REST::NotificationSerializer
|
||||
end
|
||||
|
||||
def clear
|
||||
Notification.where(account: current_account).delete_all
|
||||
current_account.notifications.delete_all
|
||||
render_empty
|
||||
end
|
||||
|
||||
def dismiss
|
||||
Notification.find_by!(account: current_account, id: params[:id]).destroy!
|
||||
current_account.notifications.find_by!(id: params[:id]).destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_notifications
|
||||
cache_collection paginated_notifications, Notification
|
||||
end
|
||||
|
||||
def paginated_notifications
|
||||
browserable_account_notifications.paginate_by_max_id(
|
||||
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def browserable_account_notifications
|
||||
current_account.notifications.browserable(exclude_types)
|
||||
end
|
||||
|
||||
def target_statuses_from_notifications
|
||||
@notifications.reject { |notification| notification.target_status.nil? }.map(&:target_status)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
unless @notifications.empty?
|
||||
api_v1_notifications_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @notifications.empty?
|
||||
api_v1_notifications_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@notifications.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@notifications.first.id
|
||||
end
|
||||
|
||||
def exclude_types
|
||||
val = params.permit(exclude_types: [])[:exclude_types] || []
|
||||
val = [val] unless val.is_a?(Enumerable)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ReportsController < ApiController
|
||||
class Api::V1::ReportsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, except: [:create]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create]
|
||||
before_action :require_user!
|
||||
@@ -8,23 +8,37 @@ class Api::V1::ReportsController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@reports = Report.where(account: current_account)
|
||||
@reports = current_account.reports
|
||||
render json: @reports, each_serializer: REST::ReportSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
|
||||
@report = ReportService.new.call(
|
||||
current_account,
|
||||
reported_account,
|
||||
status_ids: reported_status_ids,
|
||||
comment: report_params[:comment],
|
||||
forward: report_params[:forward]
|
||||
)
|
||||
|
||||
@report = Report.create!(account: current_account,
|
||||
target_account: Account.find(report_params[:account_id]),
|
||||
status_ids: Status.find(status_ids).pluck(:id),
|
||||
comment: report_params[:comment])
|
||||
|
||||
render :show
|
||||
render json: @report, serializer: REST::ReportSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reported_status_ids
|
||||
Status.find(status_ids).pluck(:id)
|
||||
end
|
||||
|
||||
def status_ids
|
||||
Array(report_params[:status_ids])
|
||||
end
|
||||
|
||||
def reported_account
|
||||
Account.find(report_params[:account_id])
|
||||
end
|
||||
|
||||
def report_params
|
||||
params.permit(:account_id, :comment, status_ids: [])
|
||||
params.permit(:account_id, :comment, :forward, status_ids: [])
|
||||
end
|
||||
end
|
||||
|
@@ -1,9 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::SearchController < ApiController
|
||||
class Api::V1::SearchController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
RESULTS_LIMIT = 5
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@search = OpenStruct.new(SearchService.new.call(params[:q], 5, params[:resolve] == 'true', current_account))
|
||||
@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,
|
||||
truthy_param?(:resolve),
|
||||
current_account
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@@ -0,0 +1,82 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :authorize_if_got_token
|
||||
before_action :set_status
|
||||
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
|
||||
default_accounts.merge(paginated_favourites).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account
|
||||
.includes(:favourites)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
end
|
||||
|
||||
def paginated_favourites
|
||||
Favourite.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_status_favourited_by_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_status_favourited_by_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.favourites.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.favourites.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
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)
|
||||
end
|
||||
end
|
38
app/controllers/api/v1/statuses/favourites_controller.rb
Normal file
38
app/controllers/api/v1/statuses/favourites_controller.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@status = favourited_status
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = requested_status
|
||||
@favourites_map = { @status.id => false }
|
||||
|
||||
UnfavouriteWorker.perform_async(current_user.account_id, @status.id)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, favourites_map: @favourites_map)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def favourited_status
|
||||
service_result.status.reload
|
||||
end
|
||||
|
||||
def service_result
|
||||
FavouriteService.new.call(current_user.account, requested_status)
|
||||
end
|
||||
|
||||
def requested_status
|
||||
Status.find(params[:status_id])
|
||||
end
|
||||
end
|
41
app/controllers/api/v1/statuses/mutes_controller.rb
Normal file
41
app/controllers/api/v1/statuses/mutes_controller.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::MutesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_conversation
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
current_account.mute_conversation!(@conversation)
|
||||
@mutes_map = { @conversation.id => true }
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
current_account.unmute_conversation!(@conversation)
|
||||
@mutes_map = { @conversation.id => false }
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = @status.conversation
|
||||
raise Mastodon::ValidationError if @conversation.nil?
|
||||
end
|
||||
end
|
54
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
54
app/controllers/api/v1/statuses/pins_controller.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
respond_to :json
|
||||
|
||||
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)
|
||||
|
||||
if pin
|
||||
pin.destroy!
|
||||
distribute_remove_activity!
|
||||
end
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
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)
|
||||
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)
|
||||
end
|
||||
end
|
@@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :authorize_if_got_token
|
||||
before_action :set_status
|
||||
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
|
||||
default_accounts.merge(paginated_statuses).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:statuses).references(:statuses)
|
||||
end
|
||||
|
||||
def paginated_statuses
|
||||
Status.where(reblog_of_id: @status.id).paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
if records_continue?
|
||||
api_v1_status_reblogged_by_index_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
end
|
||||
|
||||
def prev_path
|
||||
unless @accounts.empty?
|
||||
api_v1_status_reblogged_by_index_url pagination_params(since_id: pagination_since_id)
|
||||
end
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@accounts.last.statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@accounts.first.statuses.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
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)
|
||||
end
|
||||
end
|
35
app/controllers/api/v1/statuses/reblogs_controller.rb
Normal file
35
app/controllers/api/v1/statuses/reblogs_controller.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::ReblogsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
@status = ReblogService.new.call(current_user.account, status_for_reblog)
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = status_for_destroy.reblog
|
||||
@reblogs_map = { @status.id => false }
|
||||
|
||||
authorize status_for_destroy, :unreblog?
|
||||
RemovalWorker.perform_async(status_for_destroy.id)
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, reblogs_map: @reblogs_map)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_for_reblog
|
||||
Status.find params[:status_id]
|
||||
end
|
||||
|
||||
def status_for_destroy
|
||||
current_user.account.statuses.where(reblog_of_id: params[:status_id]).first!
|
||||
end
|
||||
end
|
@@ -1,17 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StatusesController < ApiController
|
||||
before_action :authorize_if_got_token, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
|
||||
before_action :require_user!, except: [:show, :context, :card, :reblogged_by, :favourited_by]
|
||||
before_action :set_status, only: [:show, :context, :card, :reblogged_by, :favourited_by, :mute, :unmute]
|
||||
before_action :set_conversation, only: [:mute, :unmute]
|
||||
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 :require_user!, except: [:show, :context, :card]
|
||||
before_action :set_status, only: [:show, :context, :card]
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
cached = Rails.cache.read(@status.cache_key)
|
||||
@status = cached unless cached.nil?
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def context
|
||||
@@ -20,45 +22,20 @@ class Api::V1::StatusesController < ApiController
|
||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
||||
loaded_descendants = cache_collection(descendants_results, Status)
|
||||
|
||||
@context = OpenStruct.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||
statuses = [@status] + @context[:ancestors] + @context[:descendants]
|
||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||
statuses = [@status] + @context.ancestors + @context.descendants
|
||||
|
||||
set_maps(statuses)
|
||||
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
|
||||
end
|
||||
|
||||
def card
|
||||
@card = PreviewCard.find_by(status: @status)
|
||||
render_empty if @card.nil?
|
||||
end
|
||||
@card = @status.preview_cards.first
|
||||
|
||||
def reblogged_by
|
||||
@accounts = Account.includes(:statuses)
|
||||
.references(:statuses)
|
||||
.merge(Status.where(reblog_of_id: @status.id)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: @accounts.last.statuses.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: @accounts.first.statuses.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
render :accounts
|
||||
end
|
||||
|
||||
def favourited_by
|
||||
@accounts = Account.includes(:favourites)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
.merge(Favourite.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
next_path = favourited_by_api_v1_status_url(pagination_params(max_id: @accounts.last.favourites.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: @accounts.first.favourites.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
render :accounts
|
||||
if @card.nil?
|
||||
render_empty
|
||||
else
|
||||
render json: @card, serializer: REST::PreviewCardSerializer
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@@ -72,70 +49,26 @@ class Api::V1::StatusesController < ApiController
|
||||
application: doorkeeper_token.application,
|
||||
idempotency: request.headers['Idempotency-Key'])
|
||||
|
||||
render :show
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status = Status.where(account_id: current_user.account).find(params[:id])
|
||||
authorize @status, :destroy?
|
||||
|
||||
RemovalWorker.perform_async(@status.id)
|
||||
|
||||
render_empty
|
||||
end
|
||||
|
||||
def reblog
|
||||
@status = ReblogService.new.call(current_user.account, Status.find(params[:id]))
|
||||
render :show
|
||||
end
|
||||
|
||||
def unreblog
|
||||
reblog = Status.where(account_id: current_user.account, reblog_of_id: params[:id]).first!
|
||||
@status = reblog.reblog
|
||||
@reblogs_map = { @status.id => false }
|
||||
|
||||
RemovalWorker.perform_async(reblog.id)
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
def favourite
|
||||
@status = FavouriteService.new.call(current_user.account, Status.find(params[:id])).status.reload
|
||||
render :show
|
||||
end
|
||||
|
||||
def unfavourite
|
||||
@status = Status.find(params[:id])
|
||||
@favourites_map = { @status.id => false }
|
||||
|
||||
UnfavouriteWorker.perform_async(current_user.account_id, @status.id)
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
def mute
|
||||
current_account.mute_conversation!(@conversation)
|
||||
|
||||
@mutes_map = { @conversation.id => true }
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
def unmute
|
||||
current_account.unmute_conversation!(@conversation)
|
||||
|
||||
@mutes_map = { @conversation.id => false }
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:id])
|
||||
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = @status.conversation
|
||||
raise Mastodon::ValidationError if @conversation.nil?
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
# Reraise in order to get a 404 instead of a 403 error code
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def status_params
|
||||
|
15
app/controllers/api/v1/streaming_controller.rb
Normal file
15
app/controllers/api/v1/streaming_controller.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StreamingController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
if Rails.configuration.x.streaming_api_base_url != request.host
|
||||
uri = URI.parse(request.url)
|
||||
uri.host = URI.parse(Rails.configuration.x.streaming_api_base_url).host
|
||||
redirect_to uri.to_s, status: 301
|
||||
else
|
||||
raise ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
end
|
@@ -1,30 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api::V1::Timelines
|
||||
class BaseController < ApiController
|
||||
respond_to :json
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
private
|
||||
|
||||
def cache_collection(raw)
|
||||
super(raw, Status)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
raise 'Override in child controllers'
|
||||
end
|
||||
|
||||
def prev_path
|
||||
raise 'Override in child controllers'
|
||||
end
|
||||
end
|
||||
end
|
@@ -1,44 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api::V1::Timelines
|
||||
class HomeController < BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
class Api::V1::Timelines::HomeController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
end
|
||||
respond_to :json
|
||||
|
||||
private
|
||||
def show
|
||||
@statuses = load_statuses
|
||||
|
||||
def load_statuses
|
||||
cached_home_statuses.tap do |statuses|
|
||||
set_maps(statuses)
|
||||
end
|
||||
end
|
||||
render json: @statuses,
|
||||
each_serializer: REST::StatusSerializer,
|
||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
|
||||
status: regeneration_in_progress? ? 206 : 200
|
||||
end
|
||||
|
||||
def cached_home_statuses
|
||||
cache_collection home_statuses
|
||||
end
|
||||
private
|
||||
|
||||
def home_statuses
|
||||
account_home_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
def load_statuses
|
||||
cached_home_statuses
|
||||
end
|
||||
|
||||
def account_home_feed
|
||||
Feed.new(:home, current_account)
|
||||
end
|
||||
def cached_home_statuses
|
||||
cache_collection home_statuses, Status
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_home_url pagination_params(max_id: @statuses.last.id)
|
||||
end
|
||||
def home_statuses
|
||||
account_home_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_home_url pagination_params(since_id: @statuses.first.id)
|
||||
end
|
||||
def account_home_feed
|
||||
HomeFeed.new(current_account)
|
||||
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_home_url pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_home_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
|
||||
|
||||
def regeneration_in_progress?
|
||||
Redis.current.exists("account:#{current_account.id}:regeneration")
|
||||
end
|
||||
end
|
||||
|
66
app/controllers/api/v1/timelines/list_controller.rb
Normal file
66
app/controllers/api/v1/timelines/list_controller.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::ListController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read }
|
||||
before_action :require_user!
|
||||
before_action :set_list
|
||||
before_action :set_statuses
|
||||
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
def show
|
||||
render json: @statuses,
|
||||
each_serializer: REST::StatusSerializer,
|
||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_list
|
||||
@list = List.where(account: current_account).find(params[:id])
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = cached_list_statuses
|
||||
end
|
||||
|
||||
def cached_list_statuses
|
||||
cache_collection list_statuses, Status
|
||||
end
|
||||
|
||||
def list_statuses
|
||||
list_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]
|
||||
)
|
||||
end
|
||||
|
||||
def list_feed
|
||||
ListFeed.new(@list)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_list_url params[:id], 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