mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-10 14:33:16 +00:00
Compare commits
1775 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c442589593 | ||
![]() |
28633a504a | ||
![]() |
ad78701b6f | ||
![]() |
1496488771 | ||
![]() |
dd3d958e75 | ||
![]() |
b363a3651d | ||
![]() |
86645fc14c | ||
![]() |
f9beecb343 | ||
![]() |
4ecfbd3920 | ||
![]() |
a315934314 | ||
![]() |
e9170e2de1 | ||
![]() |
5cfc1fabcf | ||
![]() |
786b12e379 | ||
![]() |
e7c5c25de8 | ||
![]() |
a1e8813522 | ||
![]() |
76c1446416 | ||
![]() |
8bd2c87399 | ||
![]() |
1e2d77f2c7 | ||
![]() |
fb6c22f5c2 | ||
![]() |
f7259f625f | ||
![]() |
b628a98d32 | ||
![]() |
d8fa807998 | ||
![]() |
ef66d8379c | ||
![]() |
8ee6cee36e | ||
![]() |
71b2120e5c | ||
![]() |
b10078633c | ||
![]() |
b5eebd4d2b | ||
![]() |
fdefc4d2b4 | ||
![]() |
f6b2609353 | ||
![]() |
bdffdcb12f | ||
![]() |
1ebb87a6a8 | ||
![]() |
83660ee381 | ||
![]() |
1fa72d6c44 | ||
![]() |
5a7c0d42f7 | ||
![]() |
e8d2432e6a | ||
![]() |
2af17adc34 | ||
![]() |
e97f43399b | ||
![]() |
c66c5fd73d | ||
![]() |
3c0767f543 | ||
![]() |
70cd1fdc63 | ||
![]() |
39028dde40 | ||
![]() |
6e39b5ef04 | ||
![]() |
49db8a9662 | ||
![]() |
2cfa6cb0e0 | ||
![]() |
1ae3510ede | ||
![]() |
6f1135d763 | ||
![]() |
52bc2f64f4 | ||
![]() |
b1375328e1 | ||
![]() |
9443e2cc4b | ||
![]() |
3a533c6c8d | ||
![]() |
c047014214 | ||
![]() |
68b05e994f | ||
![]() |
a203a05eb1 | ||
![]() |
68090cd8be | ||
![]() |
dd064aaa36 | ||
![]() |
e6e8974785 | ||
![]() |
498af63b85 | ||
![]() |
c357a7f8d6 | ||
![]() |
bae258925c | ||
![]() |
e8a603b18f | ||
![]() |
f00c8e3245 | ||
![]() |
153af19f55 | ||
![]() |
964916c71b | ||
![]() |
8782e860b6 | ||
![]() |
641c0c6393 | ||
![]() |
0383100b0e | ||
![]() |
87db28cebc | ||
![]() |
ac4b735c67 | ||
![]() |
6d017dbf10 | ||
![]() |
0d650780e2 | ||
![]() |
1804a87193 | ||
![]() |
9576434d47 | ||
![]() |
b804ed0cba | ||
![]() |
48451b782d | ||
![]() |
2e0a00ab46 | ||
![]() |
a9f2ec45da | ||
![]() |
c1ef1f62d5 | ||
![]() |
dbb20f76a7 | ||
![]() |
91741214e1 | ||
![]() |
8fa32ca8ba | ||
![]() |
8285194451 | ||
![]() |
392eaf1010 | ||
![]() |
c6dddbb66e | ||
![]() |
c52848b444 | ||
![]() |
0a7418e6d8 | ||
![]() |
72f2f35bfb | ||
![]() |
0f9f27972d | ||
![]() |
9f16f41678 | ||
![]() |
47fda2df2c | ||
![]() |
377289c961 | ||
![]() |
f852da50f6 | ||
![]() |
8ba1487f30 | ||
![]() |
644da36336 | ||
![]() |
fb5b8ae0a5 | ||
![]() |
fd902c04f7 | ||
![]() |
8ee8231a43 | ||
![]() |
c4128d89c9 | ||
![]() |
9954acf61d | ||
![]() |
0276354775 | ||
![]() |
dba636da7a | ||
![]() |
43e9186e5d | ||
![]() |
0338733531 | ||
![]() |
1be48d0cab | ||
![]() |
e60014ed9c | ||
![]() |
0d7f1584bc | ||
![]() |
36f01af6c4 | ||
![]() |
16057f550d | ||
![]() |
e79ecabd0a | ||
![]() |
c023ebc87a | ||
![]() |
ebc6897afb | ||
![]() |
b08ccaa5b3 | ||
![]() |
b9b1500fc5 | ||
![]() |
d28a4428b5 | ||
![]() |
6166e61638 | ||
![]() |
e5aa8c1ff3 | ||
![]() |
8837fd8c54 | ||
![]() |
ac039d5f13 | ||
![]() |
adf812efb3 | ||
![]() |
3f743b1a07 | ||
![]() |
204ff46f7e | ||
![]() |
54f9a1b43b | ||
![]() |
e9b1c1edfe | ||
![]() |
0ec6c26af3 | ||
![]() |
08597a1819 | ||
![]() |
102a7635d6 | ||
![]() |
b1fe35d7d2 | ||
![]() |
adf01b021c | ||
![]() |
aac51707d1 | ||
![]() |
aa345c4630 | ||
![]() |
70c6e09e0f | ||
![]() |
1a7fd2f446 | ||
![]() |
474464ffff | ||
![]() |
98f98249ff | ||
![]() |
af157939d9 | ||
![]() |
9d07a31380 | ||
![]() |
1cb026f962 | ||
![]() |
59dc0bd6f3 | ||
![]() |
69f298731e | ||
![]() |
af6ee7f230 | ||
![]() |
7c4393e719 | ||
![]() |
013c527406 | ||
![]() |
c644413f8a | ||
![]() |
ca3cc36549 | ||
![]() |
b2506478ba | ||
![]() |
ad32834ccd | ||
![]() |
4d29215ad3 | ||
![]() |
0544898b8b | ||
![]() |
aef1b4f5b7 | ||
![]() |
070455cad0 | ||
![]() |
6f2aba989f | ||
![]() |
ed4788a342 | ||
![]() |
71d4ce1c22 | ||
![]() |
e8868af079 | ||
![]() |
e28f86cbe5 | ||
![]() |
83d5016ca3 | ||
![]() |
672d411c2c | ||
![]() |
5ce055759f | ||
![]() |
ab7f50ce4e | ||
![]() |
f92ff6d699 | ||
![]() |
319fbbbfac | ||
![]() |
825312d4b0 | ||
![]() |
2254f47702 | ||
![]() |
9f94ddcd40 | ||
![]() |
ccf7760205 | ||
![]() |
24d943fee0 | ||
![]() |
d1fb957361 | ||
![]() |
0ea4267839 | ||
![]() |
6fcbd7c17a | ||
![]() |
1200f70ae7 | ||
![]() |
3509064801 | ||
![]() |
d4d77ace97 | ||
![]() |
3aed93711c | ||
![]() |
f53bb4cd7d | ||
![]() |
9896bed85f | ||
![]() |
2c828748a3 | ||
![]() |
1623d54ec0 | ||
![]() |
722fb1ff55 | ||
![]() |
933ee420c3 | ||
![]() |
0cdf11d6ad | ||
![]() |
a13b33d851 | ||
![]() |
8cf246e4d3 | ||
![]() |
629bb74451 | ||
![]() |
b8cc9b3290 | ||
![]() |
2c085ea044 | ||
![]() |
bb89a64af0 | ||
![]() |
c543e823ab | ||
![]() |
7a7e0ba4cd | ||
![]() |
c727701839 | ||
![]() |
1824b1fd29 | ||
![]() |
1bf8a642f0 | ||
![]() |
dc2cfd50a0 | ||
![]() |
a2a6117143 | ||
![]() |
24803db2bc | ||
![]() |
90183b6c27 | ||
![]() |
c3022fe10f | ||
![]() |
f3d60a4a6f | ||
![]() |
1dd8a99d9f | ||
![]() |
66a42c11ba | ||
![]() |
132f32dd70 | ||
![]() |
e557769a3c | ||
![]() |
d887790e86 | ||
![]() |
d0c6f30378 | ||
![]() |
ba75ba3adc | ||
![]() |
c92e21813e | ||
![]() |
076005eae2 | ||
![]() |
b10fde673d | ||
![]() |
68810643d8 | ||
![]() |
f2cfa4f482 | ||
![]() |
989ca63b59 | ||
![]() |
f2cdbefa3c | ||
![]() |
86627624f1 | ||
![]() |
c09f9a93f1 | ||
![]() |
963f4977d6 | ||
![]() |
5fde019e39 | ||
![]() |
520974e052 | ||
![]() |
3d474807bf | ||
![]() |
09208eafa4 | ||
![]() |
25c4574480 | ||
![]() |
e2c5a2abaa | ||
![]() |
a80f77a996 | ||
![]() |
1297ad759e | ||
![]() |
1fdcaaebbb | ||
![]() |
375add0c83 | ||
![]() |
a4bc438010 | ||
![]() |
1d152d2181 | ||
![]() |
250c3b0c1f | ||
![]() |
1dafd8c9dd | ||
![]() |
6637ecb460 | ||
![]() |
e9f197740d | ||
![]() |
12c0e58d9a | ||
![]() |
f7c19226cf | ||
![]() |
b64ad77e21 | ||
![]() |
37bf59f76a | ||
![]() |
129e72b914 | ||
![]() |
6d12831686 | ||
![]() |
efe8def0c6 | ||
![]() |
8224178e8c | ||
![]() |
d4bbe8b719 | ||
![]() |
c77158c8b7 | ||
![]() |
9c80b16401 | ||
![]() |
eb42425427 | ||
![]() |
00b56932de | ||
![]() |
d47199dd4b | ||
![]() |
0182fc389e | ||
![]() |
7036d5ad25 | ||
![]() |
11481df4dc | ||
![]() |
0d5123199f | ||
![]() |
1b11bb23d7 | ||
![]() |
d051366308 | ||
![]() |
7f4ec7d9cf | ||
![]() |
32b521b7f4 | ||
![]() |
44d71d59ef | ||
![]() |
1cc853059f | ||
![]() |
7efe20337c | ||
![]() |
54ad57ea95 | ||
![]() |
064d333620 | ||
![]() |
4b39bced3e | ||
![]() |
0789ec93f8 | ||
![]() |
975db93e3f | ||
![]() |
c025824f98 | ||
![]() |
9860046b04 | ||
![]() |
71a4a92dda | ||
![]() |
426ee069b5 | ||
![]() |
7fc10bc7b0 | ||
![]() |
4130d9659c | ||
![]() |
208dbb8821 | ||
![]() |
3c09fd3f89 | ||
![]() |
f7a3dd0e38 | ||
![]() |
a1c260696f | ||
![]() |
ca41a95872 | ||
![]() |
6e16cac09f | ||
![]() |
38e0a360ca | ||
![]() |
b195956ecb | ||
![]() |
a73ade526a | ||
![]() |
6ffa262546 | ||
![]() |
991796172a | ||
![]() |
71d46e26b2 | ||
![]() |
887533392f | ||
![]() |
43ccb04548 | ||
![]() |
75ae084976 | ||
![]() |
474013b48c | ||
![]() |
9d7ecf92fe | ||
![]() |
d78535eab9 | ||
![]() |
7ede5460d8 | ||
![]() |
32b5da558e | ||
![]() |
7132e660a6 | ||
![]() |
0372344d33 | ||
![]() |
37c82a3003 | ||
![]() |
ba5320671c | ||
![]() |
47512fe518 | ||
![]() |
955e75e820 | ||
![]() |
b7e967817b | ||
![]() |
198b59ca3e | ||
![]() |
b3615a803e | ||
![]() |
170668ed69 | ||
![]() |
d1d3ff087a | ||
![]() |
558c73a21e | ||
![]() |
e0f15a6875 | ||
![]() |
a13756148d | ||
![]() |
304c0417ed | ||
![]() |
3bf128e62a | ||
![]() |
84d03e4e16 | ||
![]() |
68a5724b3e | ||
![]() |
229e100dc0 | ||
![]() |
11c8177498 | ||
![]() |
bd2988bdf4 | ||
![]() |
6b066eac2c | ||
![]() |
5dda094daa | ||
![]() |
02ac18da51 | ||
![]() |
3acd87419c | ||
![]() |
e0ce4b9b6b | ||
![]() |
ef1bf8e9f2 | ||
![]() |
12c8a6498c | ||
![]() |
da60acced5 | ||
![]() |
80849fbfba | ||
![]() |
79ccba1758 | ||
![]() |
72356bd5ec | ||
![]() |
6664f16b14 | ||
![]() |
cddb69ed52 | ||
![]() |
68f951b945 | ||
![]() |
f1172ca8fe | ||
![]() |
e9d62b284a | ||
![]() |
8d4ae81ab4 | ||
![]() |
83dd09d4dc | ||
![]() |
6ab96ba647 | ||
![]() |
97b9e8849d | ||
![]() |
f1a6f4333a | ||
![]() |
3a5e310a6d | ||
![]() |
8c51a8ba94 | ||
![]() |
977164decc | ||
![]() |
67f5122ba6 | ||
![]() |
30ee67e2c6 | ||
![]() |
07741f307e | ||
![]() |
d8ea8bc3bb | ||
![]() |
f7182ddc8b | ||
![]() |
4709121f72 | ||
![]() |
dab31f7a88 | ||
![]() |
855022f4d9 | ||
![]() |
a8f12a6fd8 | ||
![]() |
ef87cd6910 | ||
![]() |
042a9c42be | ||
![]() |
6d6263ce07 | ||
![]() |
f94b1fce41 | ||
![]() |
d7cb6068b1 | ||
![]() |
8ed0408adb | ||
![]() |
0ada6e4168 | ||
![]() |
0d809652d9 | ||
![]() |
dbf14784bf | ||
![]() |
8ab2c4a9fc | ||
![]() |
4372d17114 | ||
![]() |
65f338c812 | ||
![]() |
39eff1c3ca | ||
![]() |
feb4e0a007 | ||
![]() |
24551375cf | ||
![]() |
90f7b90223 | ||
![]() |
e291f95a04 | ||
![]() |
b981175f08 | ||
![]() |
695f0c5eaa | ||
![]() |
2e4ba88ceb | ||
![]() |
3d2bc0c513 | ||
![]() |
9d4ec09e7a | ||
![]() |
2d13a41462 | ||
![]() |
b4bbea51c4 | ||
![]() |
9e2c0c4cef | ||
![]() |
48922c6aff | ||
![]() |
6d6e1ed958 | ||
![]() |
6452796879 | ||
![]() |
8cc5084ca1 | ||
![]() |
c4f47adb49 | ||
![]() |
a5a2c6dc7e | ||
![]() |
3ea1f074ab | ||
![]() |
c058c45a8e | ||
![]() |
24d3599690 | ||
![]() |
6c743831aa | ||
![]() |
3f965d83b0 | ||
![]() |
ae281f31db | ||
![]() |
e17c78b679 | ||
![]() |
ccffa11f2b | ||
![]() |
d475bcce65 | ||
![]() |
1b68020331 | ||
![]() |
e8e93b82f1 | ||
![]() |
aa04efb92a | ||
![]() |
c45ce549af | ||
![]() |
8d5b73d70d | ||
![]() |
16e36d8477 | ||
![]() |
545e8fbd0c | ||
![]() |
865a30ab0d | ||
![]() |
cc57fa4a41 | ||
![]() |
5305e939c4 | ||
![]() |
f993d7578b | ||
![]() |
65baf9b04a | ||
![]() |
050d76c010 | ||
![]() |
b135a831ea | ||
![]() |
c9a554bdca | ||
![]() |
5ec840a32f | ||
![]() |
b709ef8ac3 | ||
![]() |
4e2aa78a56 | ||
![]() |
3357ae9889 | ||
![]() |
6fa48fabb2 | ||
![]() |
9244ffc425 | ||
![]() |
82688387a8 | ||
![]() |
23238ddd95 | ||
![]() |
912268295c | ||
![]() |
face71b7b2 | ||
![]() |
7cd7445abe | ||
![]() |
1131f2c439 | ||
![]() |
8c579c5b34 | ||
![]() |
741f166407 | ||
![]() |
0c738ea0ca | ||
![]() |
6ac29ab1ac | ||
![]() |
b99abe44b0 | ||
![]() |
fbe9728f36 | ||
![]() |
3bbf3e9709 | ||
![]() |
79931bf3ae | ||
![]() |
22e2e7f02b | ||
![]() |
41d00bc28b | ||
![]() |
3e5d78cc5b | ||
![]() |
df6b808750 | ||
![]() |
aedc5f6921 | ||
![]() |
89cafb01b4 | ||
![]() |
2133f2b47e | ||
![]() |
833ea0725d | ||
![]() |
eacf6f2342 | ||
![]() |
84bca6fd54 | ||
![]() |
cbaba54e9d | ||
![]() |
d41a741e00 | ||
![]() |
03a0f7caf9 | ||
![]() |
8b34daf254 | ||
![]() |
b4394ec129 | ||
![]() |
24c25ec4f5 | ||
![]() |
94fa5b7168 | ||
![]() |
4354f84c5c | ||
![]() |
e3f0b955b8 | ||
![]() |
05f6f7d28a | ||
![]() |
64ab9be93f | ||
![]() |
a2310a06fa | ||
![]() |
79013c730d | ||
![]() |
b81c28e7dc | ||
![]() |
ce13fca0c5 | ||
![]() |
98e6dfcbcf | ||
![]() |
7cb93ef5a1 | ||
![]() |
66d9e47178 | ||
![]() |
e7dd0b37c7 | ||
![]() |
b0e63fbe1c | ||
![]() |
e96044f389 | ||
![]() |
715cbee93d | ||
![]() |
17d8e2b6e3 | ||
![]() |
bd9223f0b9 | ||
![]() |
40157e063d | ||
![]() |
926c67c648 | ||
![]() |
17e4345eb2 | ||
![]() |
9ed6a14d45 | ||
![]() |
1a1f3f037d | ||
![]() |
3032d9d0dd | ||
![]() |
a20686f593 | ||
![]() |
ae3b7dd28d | ||
![]() |
8f59b63176 | ||
![]() |
a97647158c | ||
![]() |
49b6a49c76 | ||
![]() |
d4944a2467 | ||
![]() |
91db45b197 | ||
![]() |
7a70d95435 | ||
![]() |
1326c8cb1d | ||
![]() |
199acce481 | ||
![]() |
b1b949f16c | ||
![]() |
6463415e06 | ||
![]() |
22ec828951 | ||
![]() |
13b13c8726 | ||
![]() |
5679bb5394 | ||
![]() |
1fc66c1970 | ||
![]() |
57a6c5f4e9 | ||
![]() |
65c67fb827 | ||
![]() |
ca4139be07 | ||
![]() |
ffc853c086 | ||
![]() |
577e407ffc | ||
![]() |
b68c622a07 | ||
![]() |
7bc301e184 | ||
![]() |
dee744c793 | ||
![]() |
e097f0e9eb | ||
![]() |
6027764c31 | ||
![]() |
b754e28190 | ||
![]() |
cba4682ced | ||
![]() |
b92f42a6bd | ||
![]() |
8dea1c422a | ||
![]() |
bc32ff9b57 | ||
![]() |
20162a821c | ||
![]() |
3159f3b2f6 | ||
![]() |
361f528c89 | ||
![]() |
bed614d44e | ||
![]() |
64d94f9e57 | ||
![]() |
8489f6c8fc | ||
![]() |
c01d219c12 | ||
![]() |
ee62ac53e1 | ||
![]() |
b33f9ea603 | ||
![]() |
b64a5a7d84 | ||
![]() |
3dc95902d7 | ||
![]() |
5810bd3310 | ||
![]() |
df2611a10f | ||
![]() |
a324edabdf | ||
![]() |
2fc38f524e | ||
![]() |
ff0990ec9f | ||
![]() |
ba0bd3da4a | ||
![]() |
f115c7b3a2 | ||
![]() |
2eaef09166 | ||
![]() |
5991caae87 | ||
![]() |
abcb9b8a61 | ||
![]() |
f47ad7814a | ||
![]() |
aadff24099 | ||
![]() |
6d94c9f4e7 | ||
![]() |
3d2f47498e | ||
![]() |
20f4b76389 | ||
![]() |
0f26ab9eee | ||
![]() |
e991a3c757 | ||
![]() |
a9cfaa6eed | ||
![]() |
a296facdea | ||
![]() |
00cd218741 | ||
![]() |
eb695e6b17 | ||
![]() |
5bf6b82625 | ||
![]() |
720889cc97 | ||
![]() |
23edac59ec | ||
![]() |
7d50942b36 | ||
![]() |
e753776930 | ||
![]() |
1d7b45093d | ||
![]() |
d43bfa95aa | ||
![]() |
678c8dfeec | ||
![]() |
5d817a758d | ||
![]() |
de19af3650 | ||
![]() |
4c2f64907b | ||
![]() |
e74d682b21 | ||
![]() |
a89ddcfd2d | ||
![]() |
6deadd596d | ||
![]() |
498372fd06 | ||
![]() |
22d33244ee | ||
![]() |
b7c3235349 | ||
![]() |
0e5be63fb3 | ||
![]() |
6a39f00745 | ||
![]() |
bdf9baa2e8 | ||
![]() |
887336f2c6 | ||
![]() |
f66d092b32 | ||
![]() |
36afb4557a | ||
![]() |
d81983f181 | ||
![]() |
ebfd48b0f2 | ||
![]() |
f31b533435 | ||
![]() |
94ae96b9bc | ||
![]() |
ff7230df06 | ||
![]() |
54aefa9014 | ||
![]() |
264ecdcc13 | ||
![]() |
9a05688326 | ||
![]() |
0b900339b0 | ||
![]() |
8451b36a72 | ||
![]() |
107a94cf6b | ||
![]() |
b4317faee2 | ||
![]() |
5f87ae101c | ||
![]() |
4ed9778c85 | ||
![]() |
9b596dbc78 | ||
![]() |
4d3758308a | ||
![]() |
58e3e43e06 | ||
![]() |
5859abf2ff | ||
![]() |
d65c3e95ad | ||
![]() |
e1d6748422 | ||
![]() |
7b9ad2c416 | ||
![]() |
dd23ba9c83 | ||
![]() |
4bbe33e0bd | ||
![]() |
470285d815 | ||
![]() |
361a6a21ba | ||
![]() |
d315a90db7 | ||
![]() |
501ced4239 | ||
![]() |
0653374c34 | ||
![]() |
05fc24c5f9 | ||
![]() |
2c70c28bbb | ||
![]() |
e2ef173b82 | ||
![]() |
324acff572 | ||
![]() |
d49fcb7ff3 | ||
![]() |
063030df82 | ||
![]() |
6e607f97a3 | ||
![]() |
e8270e2807 | ||
![]() |
9686ae7060 | ||
![]() |
2283562ebd | ||
![]() |
b2b532708e | ||
![]() |
6211130054 | ||
![]() |
65c553ab59 | ||
![]() |
b4e56822c7 | ||
![]() |
33f3a4c4c8 | ||
![]() |
70e14c1ed0 | ||
![]() |
19346fd5f8 | ||
![]() |
758d2da887 | ||
![]() |
3b5540a437 | ||
![]() |
1bc28709cc | ||
![]() |
04a9252a93 | ||
![]() |
74ee96505a | ||
![]() |
ee65f77a7e | ||
![]() |
902aab1245 | ||
![]() |
97b9994743 | ||
![]() |
e5fd61a84e | ||
![]() |
e28b64ac2d | ||
![]() |
dfa4a97dd8 | ||
![]() |
c2defe0e4c | ||
![]() |
0479efdbb6 | ||
![]() |
ef879a532f | ||
![]() |
445aa4ac72 | ||
![]() |
1326088110 | ||
![]() |
8a3bed1933 | ||
![]() |
aa575341c2 | ||
![]() |
99f93e675a | ||
![]() |
0e3ff680d3 | ||
![]() |
e9fe01e2a6 | ||
![]() |
c43508b3e0 | ||
![]() |
02db065571 | ||
![]() |
59e189ad3c | ||
![]() |
dd6c573cc3 | ||
![]() |
803a8be998 | ||
![]() |
c93b2c6809 | ||
![]() |
2a181f56e3 | ||
![]() |
94d71c992e | ||
![]() |
e1dbbf6c9d | ||
![]() |
3edac14f02 | ||
![]() |
81b88095b4 | ||
![]() |
2eb6d815d6 | ||
![]() |
8c3eeb4d29 | ||
![]() |
38f5e74122 | ||
![]() |
40bb8ec325 | ||
![]() |
ef8f62c382 | ||
![]() |
9bba2aab33 | ||
![]() |
2453b94198 | ||
![]() |
90bf67f053 | ||
![]() |
2fc4475ea3 | ||
![]() |
2e9b2df570 | ||
![]() |
82acef50b0 | ||
![]() |
a72c7f6cc1 | ||
![]() |
958953a687 | ||
![]() |
5390edc2aa | ||
![]() |
225b18e742 | ||
![]() |
ef870007e9 | ||
![]() |
b892b15ba6 | ||
![]() |
9d3daa847a | ||
![]() |
d15879312e | ||
![]() |
fe4cf75ece | ||
![]() |
0a5bbf5ac6 | ||
![]() |
7825cd1bdb | ||
![]() |
1960aac90b | ||
![]() |
a2981a0997 | ||
![]() |
469cfc5430 | ||
![]() |
0284e77e5f | ||
![]() |
5eba86e2d1 | ||
![]() |
cf25d4fe4b | ||
![]() |
c03e3129a0 | ||
![]() |
57b9dfd53e | ||
![]() |
d232fa5b14 | ||
![]() |
936d3a7de9 | ||
![]() |
e44e5d156d | ||
![]() |
8ddefbd2af | ||
![]() |
290d57d6d9 | ||
![]() |
f7b1769e8a | ||
![]() |
539a06f189 | ||
![]() |
ffc568589c | ||
![]() |
0099907600 | ||
![]() |
547658f086 | ||
![]() |
4ad5d8e6e5 | ||
![]() |
dc21104c04 | ||
![]() |
9d5cbbbf0f | ||
![]() |
6bce43cdb8 | ||
![]() |
795d465f8d | ||
![]() |
8ef546fe6b | ||
![]() |
30e334b51a | ||
![]() |
e30001bc80 | ||
![]() |
2a5853989f | ||
![]() |
e74774e366 | ||
![]() |
8c59fbe41b | ||
![]() |
2f98134ac6 | ||
![]() |
d213c585ff | ||
![]() |
4a6cf67c46 | ||
![]() |
24ec83ee52 | ||
![]() |
2d97215aad | ||
![]() |
7a6a898ca1 | ||
![]() |
f4f444528a | ||
![]() |
9c2d5b534f | ||
![]() |
31250a89b5 | ||
![]() |
7866745a6b | ||
![]() |
95dcd69542 | ||
![]() |
211518b65d | ||
![]() |
aff5182375 | ||
![]() |
98d703ac91 | ||
![]() |
c59890cda3 | ||
![]() |
2454a81e71 | ||
![]() |
41e8eaa872 | ||
![]() |
a704e1991c | ||
![]() |
f71a855e2d | ||
![]() |
fef446d22c | ||
![]() |
966b816382 | ||
![]() |
9db26db495 | ||
![]() |
46e13dd81c | ||
![]() |
2fe7172002 | ||
![]() |
420ffdfb62 | ||
![]() |
725a68d273 | ||
![]() |
9041ce3c18 | ||
![]() |
6d5a1fbe1d | ||
![]() |
325b55485e | ||
![]() |
bedf808e02 | ||
![]() |
dc43699b77 | ||
![]() |
72d7297bc4 | ||
![]() |
fb34d9c590 | ||
![]() |
f6ac0e9d49 | ||
![]() |
d489fa36f2 | ||
![]() |
207029eb2f | ||
![]() |
ddfcf1c092 | ||
![]() |
26b5f45564 | ||
![]() |
5c0e525e80 | ||
![]() |
8fedd9b19a | ||
![]() |
f944fc7034 | ||
![]() |
e1f278cdf2 | ||
![]() |
7169b270ae | ||
![]() |
04492e7f93 | ||
![]() |
2bf5aee469 | ||
![]() |
2cabbae132 | ||
![]() |
feedab26f0 | ||
![]() |
5c379a973e | ||
![]() |
b155c62aa2 | ||
![]() |
e30b9083cb | ||
![]() |
eaf1ea3155 | ||
![]() |
551339401d | ||
![]() |
5fa034fe68 | ||
![]() |
88bf1f0828 | ||
![]() |
5a100bf38f | ||
![]() |
d2ce9a6064 | ||
![]() |
64719aa4ee | ||
![]() |
f41bc1de9d | ||
![]() |
db269a4c0a | ||
![]() |
b021cfc91e | ||
![]() |
debe6c0545 | ||
![]() |
b57687083f | ||
![]() |
00dbefdbbf | ||
![]() |
8cf27d0fbb | ||
![]() |
cadda2f957 | ||
![]() |
84164270c6 | ||
![]() |
26a3081699 | ||
![]() |
ce23342d72 | ||
![]() |
e245633ffa | ||
![]() |
d25de5b757 | ||
![]() |
15da6c3c83 | ||
![]() |
37b79c638f | ||
![]() |
c53ef51362 | ||
![]() |
e4c2cf54ed | ||
![]() |
d267e022c9 | ||
![]() |
6a95b60ea8 | ||
![]() |
268b7806f1 | ||
![]() |
d90d68bddf | ||
![]() |
43f616a1c8 | ||
![]() |
e5655a5f65 | ||
![]() |
102807519b | ||
![]() |
d399244d4d | ||
![]() |
d50110a17a | ||
![]() |
894b96cf1f | ||
![]() |
bdc9cb27e2 | ||
![]() |
4960193ed0 | ||
![]() |
e71fb450e0 | ||
![]() |
b56e90d049 | ||
![]() |
bff53a637e | ||
![]() |
3aaa388ce4 | ||
![]() |
febcd0a76c | ||
![]() |
5f486a1424 | ||
![]() |
cfb473c204 | ||
![]() |
6b519cfefa | ||
![]() |
06f879ce9b | ||
![]() |
f5c4022a20 | ||
![]() |
cde3167d09 | ||
![]() |
479feafb16 | ||
![]() |
ebde60ca82 | ||
![]() |
20531d1e07 | ||
![]() |
33643c4c07 | ||
![]() |
93f0427b8a | ||
![]() |
6405883eed | ||
![]() |
298ca53593 | ||
![]() |
288e61dfdf | ||
![]() |
b1692628de | ||
![]() |
5a3fb4594b | ||
![]() |
3a60b53e9a | ||
![]() |
db97197685 | ||
![]() |
68e5f0c452 | ||
![]() |
2a15bf4ede | ||
![]() |
93bc48d649 | ||
![]() |
26c692d45b | ||
![]() |
2788808b07 | ||
![]() |
8d3bd919d8 | ||
![]() |
d065d02d88 | ||
![]() |
fc83fdc993 | ||
![]() |
12d0533bd7 | ||
![]() |
52c6f6dffc | ||
![]() |
d103be09ab | ||
![]() |
70f8fb6d08 | ||
![]() |
f1d4be1e75 | ||
![]() |
fbea3a64cc | ||
![]() |
c7f208eecf | ||
![]() |
e2eceaf2fa | ||
![]() |
d75c4bf53e | ||
![]() |
405fa6de55 | ||
![]() |
32fd40d689 | ||
![]() |
4d5ae1069c | ||
![]() |
ea5805c8a9 | ||
![]() |
e3d97006a2 | ||
![]() |
47a1f72ee6 | ||
![]() |
ea53faeb3f | ||
![]() |
a3ef72a541 | ||
![]() |
5cd80b7836 | ||
![]() |
58a029335b | ||
![]() |
8f1b41622b | ||
![]() |
62dc303d3c | ||
![]() |
c433fd01a6 | ||
![]() |
efd485cb46 | ||
![]() |
4fc8c69129 | ||
![]() |
1248c4d1f4 | ||
![]() |
6b52227fcc | ||
![]() |
fb0856458b | ||
![]() |
6df24b4bc2 | ||
![]() |
b07cd11f4c | ||
![]() |
db59f37269 | ||
![]() |
7a50fd8849 | ||
![]() |
447d12aa08 | ||
![]() |
773d58eb70 | ||
![]() |
dd25023f8c | ||
![]() |
e918d4e3e8 | ||
![]() |
506a70d0d8 | ||
![]() |
aa10e8f399 | ||
![]() |
4c597d5e2c | ||
![]() |
46329d297a | ||
![]() |
5c81921ef5 | ||
![]() |
0b4e5b64f9 | ||
![]() |
4e58570f73 | ||
![]() |
7d20c12913 | ||
![]() |
dd2cb77f1a | ||
![]() |
83a271c003 | ||
![]() |
57373ee344 | ||
![]() |
8a2ebcb8ef | ||
![]() |
804e6832d3 | ||
![]() |
3a47dd78ac | ||
![]() |
08f8bc4dfa | ||
![]() |
8c240ef042 | ||
![]() |
1cf30717db | ||
![]() |
38a3e8d0e3 | ||
![]() |
b7bc7cd087 | ||
![]() |
5ffc17415c | ||
![]() |
6d5e6a6818 | ||
![]() |
d8f1881ab0 | ||
![]() |
23a0d91126 | ||
![]() |
aff2b11394 | ||
![]() |
9701c6f148 | ||
![]() |
116f5826d3 | ||
![]() |
9692ffb911 | ||
![]() |
9e164c532f | ||
![]() |
a9643cb7e7 | ||
![]() |
334d306ccc | ||
![]() |
1be5269151 | ||
![]() |
4af91e564d | ||
![]() |
5cb54f7c5e | ||
![]() |
c3419d146a | ||
![]() |
8dce5138dd | ||
![]() |
7dca242795 | ||
![]() |
3b1ba46c93 | ||
![]() |
745391092c | ||
![]() |
95caa8bc4e | ||
![]() |
5212cd43ec | ||
![]() |
c3e80784ee | ||
![]() |
5553941666 | ||
![]() |
bc0fb83ce5 | ||
![]() |
44f5f1f0a5 | ||
![]() |
b1c9cff71b | ||
![]() |
a85a9f98d9 | ||
![]() |
107b2afb74 | ||
![]() |
4b6c955e5b | ||
![]() |
cefa829c1d | ||
![]() |
7481d94bb6 | ||
![]() |
cf820b2eca | ||
![]() |
f79b96a5ef | ||
![]() |
a29fc4b44a | ||
![]() |
77587913ce | ||
![]() |
91e4a09f24 | ||
![]() |
c5cd9bda48 | ||
![]() |
7c03327a17 | ||
![]() |
a3d2849d15 | ||
![]() |
3f4f6317d4 | ||
![]() |
005b2ba32f | ||
![]() |
e53c8fbaf8 | ||
![]() |
7ffda06b6a | ||
![]() |
796187c1e8 | ||
![]() |
2d545c5fe5 | ||
![]() |
1e70da5e3c | ||
![]() |
2beab34ca4 | ||
![]() |
6aa565b319 | ||
![]() |
51bbca7723 | ||
![]() |
85668becde | ||
![]() |
82183d8a79 | ||
![]() |
bd481204b5 | ||
![]() |
9c85825ac6 | ||
![]() |
8e24c4801d | ||
![]() |
5b291fcbe4 | ||
![]() |
32aa83e9d7 | ||
![]() |
80f72ee501 | ||
![]() |
0091459369 | ||
![]() |
6646a0a9fa | ||
![]() |
8a2d764d34 | ||
![]() |
93f3c724ae | ||
![]() |
ea743d68f3 | ||
![]() |
e97335191d | ||
![]() |
d3b637c757 | ||
![]() |
65d9171c7d | ||
![]() |
59384282ed | ||
![]() |
b868e30fdf | ||
![]() |
df63470ea6 | ||
![]() |
f8f31826bd | ||
![]() |
b53bf23df0 | ||
![]() |
2bfcda172d | ||
![]() |
956873362e | ||
![]() |
81271a18c5 | ||
![]() |
52a5c8ae05 | ||
![]() |
2df86d6413 | ||
![]() |
79cbb6160d | ||
![]() |
f73df1f2b0 | ||
![]() |
469c8f8db1 | ||
![]() |
9a0166c3f3 | ||
![]() |
a1d9c3fb99 | ||
![]() |
e44b78413a | ||
![]() |
5c50d89a86 | ||
![]() |
7261951791 | ||
![]() |
4b4de02fee | ||
![]() |
ffaf458f99 | ||
![]() |
1b2c952471 | ||
![]() |
51ecc7326f | ||
![]() |
0885c31633 | ||
![]() |
db146046c4 | ||
![]() |
3178acc5cb | ||
![]() |
8eee7ae4c3 | ||
![]() |
4a9c49ee43 | ||
![]() |
a6fc776c6f | ||
![]() |
8dcfd3f246 | ||
![]() |
607f65a0a5 | ||
![]() |
b18caff5b6 | ||
![]() |
e155aab39e | ||
![]() |
8b24085f8e | ||
![]() |
38c27a7708 | ||
![]() |
d2cc28813f | ||
![]() |
2a6a418f48 | ||
![]() |
e380e803a8 | ||
![]() |
3dcf5e12b1 | ||
![]() |
11786f1114 | ||
![]() |
4ebdfed8ea | ||
![]() |
9e90248abc | ||
![]() |
45149cd5e1 | ||
![]() |
7543c90642 | ||
![]() |
004e120e02 | ||
![]() |
7f866daf63 | ||
![]() |
2deab346b3 | ||
![]() |
a0c76f6017 | ||
![]() |
ac59ed39dd | ||
![]() |
8962f11578 | ||
![]() |
329a9e1ac1 | ||
![]() |
4bd969e4bb | ||
![]() |
bc63f2b908 | ||
![]() |
ab4874ff13 | ||
![]() |
b37e48d5c8 | ||
![]() |
5be938e661 | ||
![]() |
1c23ebd62f | ||
![]() |
531ca4b968 | ||
![]() |
092e60eb7d | ||
![]() |
febdfefafe | ||
![]() |
a3baae0b99 | ||
![]() |
3af6739f21 | ||
![]() |
a19141441d | ||
![]() |
29b355b699 | ||
![]() |
9e2f930eed | ||
![]() |
da208771b9 | ||
![]() |
69b8a0b9bf | ||
![]() |
0aa9bb8130 | ||
![]() |
da4e55eb17 | ||
![]() |
4a44c47982 | ||
![]() |
3db8498903 | ||
![]() |
998cf0dd53 | ||
![]() |
72abf05269 | ||
![]() |
c20824fa76 | ||
![]() |
ea01ecd441 | ||
![]() |
2a0951e987 | ||
![]() |
1ae574e32a | ||
![]() |
9f03e5b53a | ||
![]() |
68c9f91ccb | ||
![]() |
e1d7efadc0 | ||
![]() |
bfe73e153d | ||
![]() |
50013b10a5 | ||
![]() |
7c56517c7c | ||
![]() |
87849d739e | ||
![]() |
6356870dae | ||
![]() |
7b608b41f2 | ||
![]() |
e9462960a7 | ||
![]() |
e2f085e2b2 | ||
![]() |
a9a8b6b701 | ||
![]() |
4a2813158d | ||
![]() |
ee4edbb94f | ||
![]() |
74da9e9281 | ||
![]() |
77a44e61a8 | ||
![]() |
f9451c5614 | ||
![]() |
0db7558822 | ||
![]() |
3a762cddf6 | ||
![]() |
3c4a83fc62 | ||
![]() |
68d818121d | ||
![]() |
50449ae7ac | ||
![]() |
d517fa5ab7 | ||
![]() |
53885b0fdb | ||
![]() |
99637f2deb | ||
![]() |
44d9dc4bb0 | ||
![]() |
f3f6b65db4 | ||
![]() |
22c1b6f3ee | ||
![]() |
34cd7d6585 | ||
![]() |
2cfc2a777a | ||
![]() |
4f6edc7596 | ||
![]() |
e0f6292492 | ||
![]() |
a8b0152bc5 | ||
![]() |
1d680f1941 | ||
![]() |
d155763014 | ||
![]() |
8d4ca95163 | ||
![]() |
2499cd01db | ||
![]() |
4fb3dc0363 | ||
![]() |
846c89b66e | ||
![]() |
f98972e4eb | ||
![]() |
54e2030146 | ||
![]() |
3a4aed9890 | ||
![]() |
9b8d1fb6d1 | ||
![]() |
a8b2b474d7 | ||
![]() |
c6c8e7e6ab | ||
![]() |
91c75a6361 | ||
![]() |
51a92427ce | ||
![]() |
b0fbb71759 | ||
![]() |
8e2c642d44 | ||
![]() |
4e2c15b45d | ||
![]() |
cbae00ad23 | ||
![]() |
4148b68430 | ||
![]() |
f4b463ecb1 | ||
![]() |
ce1501c3a7 | ||
![]() |
1ce2dc3d13 | ||
![]() |
3c7f3b190c | ||
![]() |
6b1ea8dd2c | ||
![]() |
1bf6195763 | ||
![]() |
78e0d6a11a | ||
![]() |
f22a2aab40 | ||
![]() |
15669fcf75 | ||
![]() |
2c36283a89 | ||
![]() |
d34e9eaf17 | ||
![]() |
a16d83ffef | ||
![]() |
67a8d4638c | ||
![]() |
6a351e2247 | ||
![]() |
5a142060e1 | ||
![]() |
242221c11f | ||
![]() |
ee1cbda226 | ||
![]() |
35e57138f1 | ||
![]() |
0c690511c2 | ||
![]() |
b6c2923cf7 | ||
![]() |
c3fc12c2b4 | ||
![]() |
c0264c8013 | ||
![]() |
b3243ef41c | ||
![]() |
7ad44e22ed | ||
![]() |
bbe9dcfade | ||
![]() |
5bbcb1bb2e | ||
![]() |
e0863fd766 | ||
![]() |
5c4e224b66 | ||
![]() |
7d6fd68efd | ||
![]() |
fd7bcfa749 | ||
![]() |
fdfbf6e04e | ||
![]() |
927c7bb6bb | ||
![]() |
82e046ea06 | ||
![]() |
c12b85e7a9 | ||
![]() |
8233293429 | ||
![]() |
9715bd796f | ||
![]() |
edf62d4fe3 | ||
![]() |
dd937e115a | ||
![]() |
efcd4ea5de | ||
![]() |
b0634b2943 | ||
![]() |
82d2ce293d | ||
![]() |
fbd283329e | ||
![]() |
24554bb6b9 | ||
![]() |
ef39398b82 | ||
![]() |
4cf031ee13 | ||
![]() |
1eb752fb38 | ||
![]() |
e8672e27e8 | ||
![]() |
c692f69dba | ||
![]() |
ea9b10d112 | ||
![]() |
dbbf450ef5 | ||
![]() |
08dd11f8d4 | ||
![]() |
612d6182e4 | ||
![]() |
c93c1f38b7 | ||
![]() |
c0f5705c34 | ||
![]() |
081244f692 | ||
![]() |
f16fbc069a | ||
![]() |
3bdfa3eb4c | ||
![]() |
9b82bedc6f | ||
![]() |
9712518b2f | ||
![]() |
af7d6e59af | ||
![]() |
02fe176fd4 | ||
![]() |
7d6da219c0 | ||
![]() |
535866218e | ||
![]() |
f9582bcfc3 | ||
![]() |
839a4e27a9 | ||
![]() |
6ffa251d6a | ||
![]() |
098dd91523 | ||
![]() |
ed04547de2 | ||
![]() |
79a9069deb | ||
![]() |
7cbdcd7b3f | ||
![]() |
a8a16695f1 | ||
![]() |
590b7b2bb5 | ||
![]() |
8e3fd1b823 | ||
![]() |
1fbd106af9 | ||
![]() |
a557f9bbaa | ||
![]() |
00a8a5467c | ||
![]() |
3201485df2 | ||
![]() |
655c4a5ebb | ||
![]() |
2e936785b7 | ||
![]() |
68bfa7ae3f | ||
![]() |
f9fef9864a | ||
![]() |
ca06156087 | ||
![]() |
a9f2e261d3 | ||
![]() |
7b5adb0d64 | ||
![]() |
ba18289477 | ||
![]() |
28466865c1 | ||
![]() |
441dcd7396 | ||
![]() |
7c50e53baa | ||
![]() |
79cfffda3b | ||
![]() |
d94b2b1c39 | ||
![]() |
8770905186 | ||
![]() |
b01e8f4a9b | ||
![]() |
f35de1ed0d | ||
![]() |
b648c64e2e | ||
![]() |
d2fbf42b0e | ||
![]() |
344e2903b3 | ||
![]() |
d31d988e24 | ||
![]() |
aa455aaa49 | ||
![]() |
28d309fd86 | ||
![]() |
dd56292d1f | ||
![]() |
87c892191a | ||
![]() |
1acc54cfc7 | ||
![]() |
03bf4cde55 | ||
![]() |
d826919aeb | ||
![]() |
0ac43700d8 | ||
![]() |
22813dbc6e | ||
![]() |
32184e3f04 | ||
![]() |
7a08fe91b5 | ||
![]() |
22caddaa24 | ||
![]() |
aee64a465c | ||
![]() |
d014024dc5 | ||
![]() |
545a6b9eed | ||
![]() |
85792cc375 | ||
![]() |
3f8f06eb46 | ||
![]() |
db749c7d2b | ||
![]() |
668ce14ff2 | ||
![]() |
ca2a7d66b8 | ||
![]() |
1992c2a4fa | ||
![]() |
c4c9f2bf47 | ||
![]() |
3febe7d020 | ||
![]() |
3f9ac22af3 | ||
![]() |
efada38271 | ||
![]() |
14234d2bda | ||
![]() |
d65e246192 | ||
![]() |
0085e8c427 | ||
![]() |
ce5c33c65d | ||
![]() |
978142ac9e | ||
![]() |
f19fd0b889 | ||
![]() |
1712543c68 | ||
![]() |
02748f172d | ||
![]() |
60bb51eef8 | ||
![]() |
a7673d361d | ||
![]() |
3ac478472e | ||
![]() |
a837f7d693 | ||
![]() |
efe4e72f93 | ||
![]() |
9067a7f696 | ||
![]() |
bfef66276a | ||
![]() |
c68eb82ffa | ||
![]() |
ecdf4ea062 | ||
![]() |
6305b6e747 | ||
![]() |
cf4595967b | ||
![]() |
a596f3479b | ||
![]() |
3d00b95359 | ||
![]() |
504ea31bd3 | ||
![]() |
d19eaebd5e | ||
![]() |
fd0793dd0e | ||
![]() |
3cf6681736 | ||
![]() |
74478888ff | ||
![]() |
519630bc03 | ||
![]() |
02e78a7dac | ||
![]() |
5cf37248cc | ||
![]() |
a8edc82471 | ||
![]() |
efc85e39a0 | ||
![]() |
4130bda12e | ||
![]() |
7200f9d25c | ||
![]() |
92a4423ab7 | ||
![]() |
03aa09561d | ||
![]() |
1bd9306ced | ||
![]() |
4f2d060f94 | ||
![]() |
148b1ff230 | ||
![]() |
e2c7462852 | ||
![]() |
ed33064e7b | ||
![]() |
3c71ee9b90 | ||
![]() |
234af14966 | ||
![]() |
d580607510 | ||
![]() |
42f6a36f21 | ||
![]() |
bc5b9d98a5 | ||
![]() |
0636bcdbe1 | ||
![]() |
708919ee93 | ||
![]() |
4bf6af3b61 | ||
![]() |
e0a49af47d | ||
![]() |
b51e0bc3c7 | ||
![]() |
d56aa9031a | ||
![]() |
6445facb8c | ||
![]() |
8fa89c2db1 | ||
![]() |
43702b95a2 | ||
![]() |
315c170910 | ||
![]() |
d6241f6751 | ||
![]() |
bcb3b627b9 | ||
![]() |
da279df8ae | ||
![]() |
a4560d9278 | ||
![]() |
3614e9a30b | ||
![]() |
ad68686bec | ||
![]() |
f721e76d13 | ||
![]() |
f6daf73286 | ||
![]() |
f12b3a9cef | ||
![]() |
32e23ef630 | ||
![]() |
5e4ca0881e | ||
![]() |
bf4ad7e200 | ||
![]() |
9ba4c8ef66 | ||
![]() |
a70ae28644 | ||
![]() |
39364346bb | ||
![]() |
094e2172ec | ||
![]() |
ded799f913 | ||
![]() |
e4e35ab134 | ||
![]() |
e76aff7de5 | ||
![]() |
3222c19d45 | ||
![]() |
12eb6b0e6d | ||
![]() |
66266ec6fc | ||
![]() |
19c6cb82f6 | ||
![]() |
c72083ad68 | ||
![]() |
2a369a8977 | ||
![]() |
7d52b24569 | ||
![]() |
58c5068bda | ||
![]() |
801cd731be | ||
![]() |
c8c916e657 | ||
![]() |
2131e61548 | ||
![]() |
12d9ef6003 | ||
![]() |
425311e1d9 | ||
![]() |
2e35b15b4d | ||
![]() |
cfd2879ba4 | ||
![]() |
342055cb15 | ||
![]() |
6615f17b48 | ||
![]() |
7b7a29867b | ||
![]() |
30aa0df88c | ||
![]() |
7a2a345c08 | ||
![]() |
f646fb0259 | ||
![]() |
bf79174829 | ||
![]() |
ab133c5e30 | ||
![]() |
796dd57e1b | ||
![]() |
ca45f896dd | ||
![]() |
58c40caeb4 | ||
![]() |
72a4da83fd | ||
![]() |
8dc7f8533d | ||
![]() |
96498a09c2 | ||
![]() |
3ad45ced5e | ||
![]() |
e35655cf55 | ||
![]() |
fbcd14ff7e | ||
![]() |
25387dc423 | ||
![]() |
a1143c522b | ||
![]() |
635da00cfa | ||
![]() |
d29eddc401 | ||
![]() |
17c02c9210 | ||
![]() |
b82c791770 | ||
![]() |
1ee149d015 | ||
![]() |
86b45a4a48 | ||
![]() |
21f6780498 | ||
![]() |
8b223f3b15 | ||
![]() |
f9dc7842de | ||
![]() |
f57c82ed35 | ||
![]() |
d58173e45b | ||
![]() |
3708039856 | ||
![]() |
b85d7eb7a3 | ||
![]() |
4934242aca | ||
![]() |
0fcf227a92 | ||
![]() |
48ea7552dd | ||
![]() |
b9b26490e7 | ||
![]() |
23a2366090 | ||
![]() |
0262310c88 | ||
![]() |
ebbccb196a | ||
![]() |
c521a62b1e | ||
![]() |
1cb303ca75 | ||
![]() |
e5bea68b20 | ||
![]() |
6fddf1610c | ||
![]() |
7420f59f76 | ||
![]() |
30c2a6c01c | ||
![]() |
836810e88b | ||
![]() |
7135f513a4 | ||
![]() |
6cf87762a4 | ||
![]() |
88ff1c8e7e | ||
![]() |
ada5baf0b2 | ||
![]() |
04b7046be1 | ||
![]() |
be43b01eb1 | ||
![]() |
2381ed55d7 | ||
![]() |
346a27b6fc | ||
![]() |
752d49eefe | ||
![]() |
de37bc7722 | ||
![]() |
360b6d3a44 | ||
![]() |
5c06fe4902 | ||
![]() |
4b80ff92bd | ||
![]() |
459be1f4e7 | ||
![]() |
ca11740dc9 | ||
![]() |
f72831b9c2 | ||
![]() |
9e8da55d99 | ||
![]() |
462e0717d6 | ||
![]() |
a98a8ee93f | ||
![]() |
a17e7d10fc | ||
![]() |
3cd308523a | ||
![]() |
75ecc6df06 | ||
![]() |
4de108e755 | ||
![]() |
986a3676d3 | ||
![]() |
4635ce2926 | ||
![]() |
c58967c3bd | ||
![]() |
9ff01403ea | ||
![]() |
9296d58024 | ||
![]() |
7f2cfcccab | ||
![]() |
eef8d2c855 | ||
![]() |
7a4370f2d9 | ||
![]() |
7f4858b339 | ||
![]() |
48f3ed738f | ||
![]() |
fa3577e04b | ||
![]() |
e4bd3162e8 | ||
![]() |
46c43d263c | ||
![]() |
7a3dea385e | ||
![]() |
429e08e3d2 | ||
![]() |
a27bafa596 | ||
![]() |
f0855fd41f | ||
![]() |
5c3a64dd50 | ||
![]() |
7ba19ecf1e | ||
![]() |
08914516d9 | ||
![]() |
6efa320feb | ||
![]() |
b702cd74f3 | ||
![]() |
72f623c391 | ||
![]() |
6b1dd1bf2a | ||
![]() |
36496f4d73 | ||
![]() |
3e901d108c | ||
![]() |
0ea9d8164b | ||
![]() |
6d62581da1 | ||
![]() |
9a7130d6da | ||
![]() |
2d8fed23e6 | ||
![]() |
1a88c05274 | ||
![]() |
1be83c6982 | ||
![]() |
74df47ad9c | ||
![]() |
ab9c62e8c7 | ||
![]() |
fd90f04f0e | ||
![]() |
7fb0880e1e | ||
![]() |
4521390163 | ||
![]() |
1333ed4d4e | ||
![]() |
a20dca7327 | ||
![]() |
8c322cca19 | ||
![]() |
91cc180cd0 | ||
![]() |
27e79da6b9 | ||
![]() |
21a8612aab | ||
![]() |
2e66dd09e2 | ||
![]() |
99f36f1b7b | ||
![]() |
04ce5939ae | ||
![]() |
9d34146aaa | ||
![]() |
35683ac154 | ||
![]() |
0ad5c212c1 | ||
![]() |
9ebed5d410 | ||
![]() |
d0753ec276 | ||
![]() |
47190087f8 | ||
![]() |
2e5abe4720 | ||
![]() |
be2d4615ab | ||
![]() |
d75088d699 | ||
![]() |
0518613dd7 | ||
![]() |
68c7782940 | ||
![]() |
4bfb8887bf | ||
![]() |
62603508c7 | ||
![]() |
7385016837 | ||
![]() |
dbddd40c1c | ||
![]() |
4517e18b79 | ||
![]() |
e4d5cc0ca6 | ||
![]() |
420ed155ba | ||
![]() |
2526b32ad3 | ||
![]() |
683db3ab25 | ||
![]() |
e68b26c8df | ||
![]() |
174c687ca0 | ||
![]() |
4252b13a4b | ||
![]() |
b66fbaeec2 | ||
![]() |
41b033e381 | ||
![]() |
30e9c7137b | ||
![]() |
37f00fb018 | ||
![]() |
2b5faa2ba3 | ||
![]() |
f2976ec9a4 | ||
![]() |
87e7ad9b8f | ||
![]() |
9fc2fc2251 | ||
![]() |
436a5d295b | ||
![]() |
e856838e0c | ||
![]() |
06fcee099a | ||
![]() |
425982841d | ||
![]() |
295ad6f19a | ||
![]() |
30a7986569 | ||
![]() |
da826fe959 | ||
![]() |
477412d62a | ||
![]() |
3e44dca0b4 | ||
![]() |
c0c34d35e2 | ||
![]() |
911d7dbbf6 | ||
![]() |
9a46329fbd | ||
![]() |
03ee08c2da | ||
![]() |
d8e907fae3 | ||
![]() |
c511cbcd6c | ||
![]() |
16addf47c7 | ||
![]() |
be413d67de | ||
![]() |
02985aae69 | ||
![]() |
49a12a2df9 | ||
![]() |
5b68f4b070 | ||
![]() |
60801ab914 | ||
![]() |
bcf5d30e01 | ||
![]() |
1c6f53e70f | ||
![]() |
a1ac2a73ff | ||
![]() |
44d92fa4f6 | ||
![]() |
9d3fcc3546 | ||
![]() |
7bd7705f59 | ||
![]() |
54a7c1898e | ||
![]() |
319109029d | ||
![]() |
297ce9ef44 | ||
![]() |
34d7c9edae | ||
![]() |
f1ef7b5b43 | ||
![]() |
0838cf1e05 | ||
![]() |
c546aa57cb | ||
![]() |
ddfb3d123b | ||
![]() |
2de018256a | ||
![]() |
766358e52b | ||
![]() |
62d65504f6 | ||
![]() |
e8b6607ece | ||
![]() |
ec663eaba1 | ||
![]() |
aaebf142aa | ||
![]() |
980970ddfd | ||
![]() |
7fc8292ea5 | ||
![]() |
03ecf5df3b | ||
![]() |
8f5cbf5370 | ||
![]() |
95d7120ce6 | ||
![]() |
baad795384 | ||
![]() |
a6d829e86f | ||
![]() |
2bd56f726a | ||
![]() |
ace4268308 | ||
![]() |
884bbf7ae2 | ||
![]() |
f5f6273d2b | ||
![]() |
32e5e1d3f1 | ||
![]() |
f8e96e761b | ||
![]() |
5d9dde3ec0 | ||
![]() |
897cb1803e | ||
![]() |
bf609090b1 | ||
![]() |
e97f4b18ad | ||
![]() |
0d06d70d11 | ||
![]() |
ea6d3ca8c1 | ||
![]() |
1f85ca2b0e | ||
![]() |
a34edc211f | ||
![]() |
88512220bb | ||
![]() |
90b5017c19 | ||
![]() |
67a2b03909 | ||
![]() |
5a99ac3cfd | ||
![]() |
c099797700 | ||
![]() |
aeae152006 | ||
![]() |
c78dc23b49 | ||
![]() |
d033920b7e | ||
![]() |
157fba4698 | ||
![]() |
e22ec25077 | ||
![]() |
1e71c4e247 | ||
![]() |
10c895dc84 | ||
![]() |
bde0f1239a | ||
![]() |
df54196a14 | ||
![]() |
4ef3a1a38d | ||
![]() |
8df867b20a | ||
![]() |
f180880f65 | ||
![]() |
870bb06994 | ||
![]() |
41227aeb95 | ||
![]() |
16b074d731 | ||
![]() |
697947116e | ||
![]() |
6ae1fcd059 | ||
![]() |
e0a44b8317 | ||
![]() |
68b545ef33 | ||
![]() |
73f66af6eb | ||
![]() |
ae667624ac | ||
![]() |
befb44a08c | ||
![]() |
8f4af29a73 | ||
![]() |
4ed3ce2377 | ||
![]() |
d482211aa6 | ||
![]() |
1392daa638 | ||
![]() |
4053cd2bff | ||
![]() |
e581c1a520 | ||
![]() |
e4b4d6d262 | ||
![]() |
2bea74e69d | ||
![]() |
871e3b25e8 | ||
![]() |
d6349c0e9a | ||
![]() |
20a147170e | ||
![]() |
d60ef3f17e | ||
![]() |
933fa81baf | ||
![]() |
883b509988 | ||
![]() |
c83eea9a58 | ||
![]() |
0a1b5df202 | ||
![]() |
90f4ffa31d | ||
![]() |
823f597f00 | ||
![]() |
a139dac18e | ||
![]() |
dfccd207bb | ||
![]() |
ff490daa58 | ||
![]() |
2271b39a5c | ||
![]() |
e7a93915de | ||
![]() |
074fb04c6a | ||
![]() |
5bc32c47a2 | ||
![]() |
f2763d978c | ||
![]() |
1657106473 | ||
![]() |
dfdc6b2871 | ||
![]() |
b06fd54c30 | ||
![]() |
080b3b6ca5 | ||
![]() |
b231c3c1bf | ||
![]() |
a20ac20302 | ||
![]() |
917a799c67 | ||
![]() |
516c97a112 | ||
![]() |
b3b3c0fe96 | ||
![]() |
87aa3467cf | ||
![]() |
75b2ac49fb | ||
![]() |
e78db58b65 | ||
![]() |
c2b498a2b0 | ||
![]() |
01e25af2e3 | ||
![]() |
e1b7382ea6 | ||
![]() |
0a599d08d8 | ||
![]() |
110aa0aa28 | ||
![]() |
7f743f3c81 | ||
![]() |
742eb549ab | ||
![]() |
babee06794 | ||
![]() |
df3b954720 | ||
![]() |
de1d8dc63a | ||
![]() |
311d2b7f3a | ||
![]() |
9de3fd60a0 | ||
![]() |
244aaf9a38 | ||
![]() |
d94a367aeb | ||
![]() |
b107fc2e65 | ||
![]() |
dc077437d5 | ||
![]() |
cff1846f80 | ||
![]() |
6f5eb22135 | ||
![]() |
b1d3c64525 | ||
![]() |
dc0b1948be | ||
![]() |
0426cb78f7 | ||
![]() |
6a5a59c28c | ||
![]() |
c3919289d2 | ||
![]() |
48f6f46072 | ||
![]() |
d547bf2f1e | ||
![]() |
1edb527072 | ||
![]() |
9074c1fac9 | ||
![]() |
77cd16f4ee | ||
![]() |
3aa38841bd | ||
![]() |
aa29e42cf8 | ||
![]() |
60b0ad0771 | ||
![]() |
5f58357bbe | ||
![]() |
097942a291 | ||
![]() |
0970cf818a | ||
![]() |
1775e151ec | ||
![]() |
9464281f29 | ||
![]() |
66c0471515 | ||
![]() |
15f6336cdd | ||
![]() |
86132f7dd3 | ||
![]() |
2a13d6824c | ||
![]() |
2e3bbb6861 | ||
![]() |
4134ccdbe0 | ||
![]() |
e5be4bf8d8 | ||
![]() |
37bcbeab4a | ||
![]() |
dea6c454fd | ||
![]() |
b16435e79f | ||
![]() |
67504dbf9c | ||
![]() |
5c910dc82e | ||
![]() |
745c4f900a | ||
![]() |
d11231a420 | ||
![]() |
97b18d10b4 | ||
![]() |
35f008aa2a | ||
![]() |
5a4f4f3e83 | ||
![]() |
a850be4ad3 | ||
![]() |
2f906ed55a | ||
![]() |
fae1de699c | ||
![]() |
c3e684b715 | ||
![]() |
393f0a0159 | ||
![]() |
05f23df3b7 | ||
![]() |
d1b20ea8f7 | ||
![]() |
bd0c826a3d | ||
![]() |
c0f46e9031 | ||
![]() |
1f3722904f | ||
![]() |
67403e7b01 | ||
![]() |
0a4a73f9a6 | ||
![]() |
6d2f865862 | ||
![]() |
029c99bd7b | ||
![]() |
18659f8163 | ||
![]() |
16597fa735 | ||
![]() |
f8a66e9915 | ||
![]() |
346cdb998c | ||
![]() |
bb05325306 | ||
![]() |
36f1ec164d | ||
![]() |
4d5b2de6c8 | ||
![]() |
699f75bb35 | ||
![]() |
bd850d1ec0 | ||
![]() |
40b4f73c18 | ||
![]() |
e05f9fed33 | ||
![]() |
29f98b361f | ||
![]() |
11a63b2db6 | ||
![]() |
4a65fa74fa | ||
![]() |
650f224328 | ||
![]() |
2e0bb35cf7 | ||
![]() |
35da47c857 | ||
![]() |
8e39cc9218 | ||
![]() |
a19a120769 | ||
![]() |
52af99ce77 | ||
![]() |
fb6d7ed475 | ||
![]() |
1fc165de02 | ||
![]() |
2391937e11 | ||
![]() |
8eae0d067d | ||
![]() |
0f37b7d6ac | ||
![]() |
bd6c96f737 | ||
![]() |
8b56509625 | ||
![]() |
009f2ebc32 | ||
![]() |
b23ff050ac | ||
![]() |
5103abc3ec | ||
![]() |
a0c7b317a7 | ||
![]() |
bc2e642272 | ||
![]() |
6ff1954bdb | ||
![]() |
6c87c76e18 | ||
![]() |
acc1973f3a | ||
![]() |
bfe3d49cca | ||
![]() |
f8ba2122f8 | ||
![]() |
5900c8c7fd | ||
![]() |
25f8003d75 | ||
![]() |
7a7a7cc448 | ||
![]() |
c40cd79c4d | ||
![]() |
9e8f099d38 | ||
![]() |
28131b4b00 | ||
![]() |
52bc33115d | ||
![]() |
b78597979a | ||
![]() |
b742ce9d09 | ||
![]() |
92c8860451 | ||
![]() |
0e58dc90d9 | ||
![]() |
e9b27852be | ||
![]() |
27f05e9016 | ||
![]() |
c772b41c36 | ||
![]() |
c19397b94a | ||
![]() |
bfb41e61a3 | ||
![]() |
227d46883f | ||
![]() |
6a39212b02 | ||
![]() |
92bb19773a | ||
![]() |
d1d2278e70 | ||
![]() |
b1c10969ac | ||
![]() |
a11b599ffa | ||
![]() |
0cc21f1ded | ||
![]() |
d74c2c583a | ||
![]() |
a1eb1a9642 | ||
![]() |
5afdb81b36 | ||
![]() |
5e4cc1a39c | ||
![]() |
f2378aca40 | ||
![]() |
7d89cea0ea | ||
![]() |
5ac64153d9 | ||
![]() |
b5e8bc4dad | ||
![]() |
51345e5133 | ||
![]() |
6d99f848a2 | ||
![]() |
7c10b0fb7a | ||
![]() |
ffa1032381 | ||
![]() |
cc70acc11c | ||
![]() |
d8eab3d81f | ||
![]() |
2404d6d1a1 | ||
![]() |
06d6b35e88 | ||
![]() |
2343ce4441 | ||
![]() |
555fb98cbb | ||
![]() |
05bc82e1ab | ||
![]() |
e9eac64896 | ||
![]() |
de422a37e8 | ||
![]() |
b0dd38433b | ||
![]() |
e15befebbd | ||
![]() |
10ea6da09f | ||
![]() |
bb0cf04d71 | ||
![]() |
22fd767425 | ||
![]() |
3b07fe1bba | ||
![]() |
9350cd31d7 | ||
![]() |
a5a3733c97 | ||
![]() |
cdf603239e | ||
![]() |
d9fbb071da | ||
![]() |
03dbebdfef | ||
![]() |
fda52b2a52 | ||
![]() |
6d6565eee7 | ||
![]() |
0d91db3d77 | ||
![]() |
258dce1256 | ||
![]() |
e8ec6667bd | ||
![]() |
f49161ab1d | ||
![]() |
3cf2d35c49 | ||
![]() |
d20a899bb9 | ||
![]() |
022c1ae6f2 | ||
![]() |
c3a38c7d8c | ||
![]() |
ff3e2c9cfa | ||
![]() |
562105c69a | ||
![]() |
c60d4ecc82 | ||
![]() |
0c16365991 | ||
![]() |
4238da6ee3 | ||
![]() |
bfabd6a2b8 | ||
![]() |
1f5bd571cd | ||
![]() |
a59160cf01 | ||
![]() |
0ce2db4f7d | ||
![]() |
498024558a | ||
![]() |
2c54b91dd1 | ||
![]() |
c40ab43dc7 | ||
![]() |
51769e0670 | ||
![]() |
1f720366e9 | ||
![]() |
ebdeac0731 | ||
![]() |
e4e07b1c34 | ||
![]() |
4fe7f213a6 | ||
![]() |
77f5b127fa | ||
![]() |
463f9197d8 | ||
![]() |
49407e7623 | ||
![]() |
cf28104317 | ||
![]() |
82e7d53d54 | ||
![]() |
160917e718 | ||
![]() |
63a959099b | ||
![]() |
12a8ac1f85 | ||
![]() |
1b247a1dc2 | ||
![]() |
aba888c4a6 | ||
![]() |
4c0e44ebbe | ||
![]() |
cc8d723e71 | ||
![]() |
d95f6f4410 | ||
![]() |
52afa94f1c | ||
![]() |
1db91ab8d9 | ||
![]() |
fe3f5375e3 | ||
![]() |
d96351a87d | ||
![]() |
0be1d332f2 | ||
![]() |
6ef510fe3f | ||
![]() |
f768a6eb16 | ||
![]() |
cec8961372 | ||
![]() |
7d6b9ccd34 | ||
![]() |
36f9c96812 | ||
![]() |
b7bb850efd | ||
![]() |
f760899b04 | ||
![]() |
b5006539c9 | ||
![]() |
d270d6dd4f | ||
![]() |
2151dfb8d6 | ||
![]() |
cf859d151b | ||
![]() |
1f65a95421 | ||
![]() |
4aa26eba53 | ||
![]() |
4a737a948a | ||
![]() |
ebab3b80c7 | ||
![]() |
33d3ca7cf1 | ||
![]() |
f0716368e6 | ||
![]() |
ea72d5ec3a | ||
![]() |
66b2bc1c84 | ||
![]() |
f91f077985 | ||
![]() |
a2c4ba2001 | ||
![]() |
4cae309f6a | ||
![]() |
65508bb0c0 | ||
![]() |
df98cf71e3 | ||
![]() |
f10d61bf86 | ||
![]() |
f73787a4b8 | ||
![]() |
7b92cf3b47 | ||
![]() |
c5f0da98e4 | ||
![]() |
44071fdbae | ||
![]() |
ec16ed37c3 | ||
![]() |
c009507912 | ||
![]() |
651846c622 | ||
![]() |
4b4bf82ea9 | ||
![]() |
489abb8173 | ||
![]() |
7e35bef97e | ||
![]() |
8ae381fd04 | ||
![]() |
efd3f59627 | ||
![]() |
454d21ab5a | ||
![]() |
e975b55c24 | ||
![]() |
e6cda8388c | ||
![]() |
0b89765e9a | ||
![]() |
f251edfc1d | ||
![]() |
7a653001b4 | ||
![]() |
b7ddf45924 | ||
![]() |
3d943f03fd | ||
![]() |
2829f72d16 | ||
![]() |
f811fcb2b2 | ||
![]() |
675d3ea5c1 | ||
![]() |
497e8d00b9 | ||
![]() |
d9973f3b70 | ||
![]() |
685067efd1 | ||
![]() |
1be55ce244 | ||
![]() |
5839ee434b | ||
![]() |
0b6d217b9e | ||
![]() |
07cd1fd1ef |
59
.annotaterb.yml
Normal file
59
.annotaterb.yml
Normal file
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
:position: before
|
||||
:position_in_additional_file_patterns: before
|
||||
:position_in_class: before
|
||||
:position_in_factory: before
|
||||
:position_in_fixture: before
|
||||
:position_in_routes: before
|
||||
:position_in_serializer: before
|
||||
:position_in_test: before
|
||||
:classified_sort: true
|
||||
:exclude_controllers: true
|
||||
:exclude_factories: true
|
||||
:exclude_fixtures: true
|
||||
:exclude_helpers: true
|
||||
:exclude_scaffolds: true
|
||||
:exclude_serializers: true
|
||||
:exclude_sti_subclasses: true
|
||||
:exclude_tests: true
|
||||
:force: false
|
||||
:format_markdown: false
|
||||
:format_rdoc: false
|
||||
:format_yard: false
|
||||
:frozen: false
|
||||
:ignore_model_sub_dir: false
|
||||
:ignore_unknown_models: false
|
||||
:include_version: false
|
||||
:show_complete_foreign_keys: false
|
||||
:show_foreign_keys: false
|
||||
:show_indexes: false
|
||||
:simple_indexes: false
|
||||
:sort: false
|
||||
:timestamp: false
|
||||
:trace: false
|
||||
:with_comment: true
|
||||
:with_column_comments: true
|
||||
:with_table_comments: true
|
||||
:active_admin: false
|
||||
:command:
|
||||
:debug: false
|
||||
:hide_default_column_types: ''
|
||||
:hide_limit_column_types: 'integer,boolean'
|
||||
:ignore_columns:
|
||||
:ignore_routes:
|
||||
:models: true
|
||||
:routes: false
|
||||
:skip_on_db_migrate: false
|
||||
:target_action: :do_annotations
|
||||
:wrapper:
|
||||
:wrapper_close:
|
||||
:wrapper_open:
|
||||
:classes_default_to_s: []
|
||||
:additional_file_patterns: []
|
||||
:model_dir:
|
||||
- app/models
|
||||
:require: []
|
||||
:root_dir:
|
||||
- ''
|
||||
|
||||
:show_check_constraints: false
|
|
@ -1,10 +1,6 @@
|
|||
[production]
|
||||
defaults
|
||||
> 0.2%
|
||||
firefox >= 78
|
||||
ios >= 15.6
|
||||
not dead
|
||||
not OperaMini all
|
||||
|
||||
[development]
|
||||
supports es6-module
|
||||
|
|
|
@ -9,7 +9,9 @@ services:
|
|||
environment:
|
||||
RAILS_ENV: development
|
||||
NODE_ENV: development
|
||||
VITE_RUBY_HOST: 0.0.0.0
|
||||
BIND: 0.0.0.0
|
||||
BOOTSNAP_CACHE_DIR: /tmp
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: '6379'
|
||||
DB_HOST: db
|
||||
|
@ -20,12 +22,14 @@ services:
|
|||
ES_HOST: es
|
||||
ES_PORT: '9200'
|
||||
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
|
||||
LOCAL_DOMAIN: ${LOCAL_DOMAIN:-localhost:3000}
|
||||
VITE_DEV_SERVER_PUBLIC: ${VITE_DEV_SERVER_PUBLIC:-localhost:3036}
|
||||
# Overrides default command so things don't shut down after the process ends.
|
||||
command: sleep infinity
|
||||
ports:
|
||||
- '127.0.0.1:3000:3000'
|
||||
- '127.0.0.1:3035:3035'
|
||||
- '127.0.0.1:4000:4000'
|
||||
- '3000:3000'
|
||||
- '3036:3036'
|
||||
- '4000:4000'
|
||||
networks:
|
||||
- external_network
|
||||
- internal_network
|
||||
|
@ -69,7 +73,7 @@ services:
|
|||
hard: -1
|
||||
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:v1.6.1
|
||||
image: libretranslate/libretranslate:v1.6.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- lt-data:/home/libretranslate/.local
|
||||
|
|
|
@ -20,3 +20,9 @@ postgres14
|
|||
redis
|
||||
elasticsearch
|
||||
chart
|
||||
.yarn/
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
|
|
@ -43,7 +43,6 @@ ES_PASS=password
|
|||
# Make sure to use `bundle exec rails secret` to generate secrets
|
||||
# -------
|
||||
SECRET_KEY_BASE=
|
||||
OTP_SECRET=
|
||||
|
||||
# Encryption secrets
|
||||
# ------------------
|
||||
|
@ -79,6 +78,9 @@ AWS_ACCESS_KEY_ID=
|
|||
AWS_SECRET_ACCESS_KEY=
|
||||
S3_ALIAS_HOST=files.example.com
|
||||
|
||||
# Optional list of hosts that are allowed to serve media for your instance
|
||||
# EXTRA_MEDIA_HOSTS=https://data.example1.com,https://data.example2.com
|
||||
|
||||
# IP and session retention
|
||||
# -----------------------
|
||||
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
||||
|
@ -86,3 +88,24 @@ S3_ALIAS_HOST=files.example.com
|
|||
# -----------------------
|
||||
IP_RETENTION_PERIOD=31556952
|
||||
SESSION_RETENTION_PERIOD=31556952
|
||||
|
||||
# Fetch All Replies Behavior
|
||||
# --------------------------
|
||||
# When a user expands a post (DetailedStatus view), fetch all of its replies
|
||||
# (default: false)
|
||||
FETCH_REPLIES_ENABLED=false
|
||||
|
||||
# Period to wait between fetching replies (in minutes)
|
||||
FETCH_REPLIES_COOLDOWN_MINUTES=15
|
||||
|
||||
# Period to wait after a post is first created before fetching its replies (in minutes)
|
||||
FETCH_REPLIES_INITIAL_WAIT_MINUTES=5
|
||||
|
||||
# Max number of replies to fetch - total, recursively through a whole reply tree
|
||||
FETCH_REPLIES_MAX_GLOBAL=1000
|
||||
|
||||
# Max number of replies to fetch - for a single post
|
||||
FETCH_REPLIES_MAX_SINGLE=500
|
||||
|
||||
# Max number of replies Collection pages to fetch - total
|
||||
FETCH_REPLIES_MAX_PAGES=500
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/build/**
|
||||
/coverage/**
|
||||
/db/**
|
||||
/lib/**
|
||||
/log/**
|
||||
/node_modules/**
|
||||
/nonobox/**
|
||||
/public/**
|
||||
!/public/embed.js
|
||||
/spec/**
|
||||
/tmp/**
|
||||
/vendor/**
|
||||
!.eslintrc.js
|
368
.eslintrc.js
368
.eslintrc.js
|
@ -1,368 +0,0 @@
|
|||
// @ts-check
|
||||
const { defineConfig } = require('eslint-define-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
root: true,
|
||||
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:promise/recommended',
|
||||
'plugin:jsdoc/recommended',
|
||||
],
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
|
||||
plugins: [
|
||||
'react',
|
||||
'jsx-a11y',
|
||||
'import',
|
||||
'promise',
|
||||
'@typescript-eslint',
|
||||
'formatjs',
|
||||
],
|
||||
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 2021,
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
configFile: false,
|
||||
presets: ['@babel/react', '@babel/env'],
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
'import/ignore': [
|
||||
'node_modules',
|
||||
'\\.(css|scss|json)$',
|
||||
],
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'consistent-return': 'error',
|
||||
'dot-notation': 'error',
|
||||
eqeqeq: ['error', 'always', { 'null': 'ignore' }],
|
||||
'indent': ['error', 2],
|
||||
'jsx-quotes': ['error', 'prefer-single'],
|
||||
'semi': ['error', 'always'],
|
||||
'no-case-declarations': 'off',
|
||||
'no-catch-shadow': 'error',
|
||||
'no-console': [
|
||||
'warn',
|
||||
{
|
||||
allow: [
|
||||
'error',
|
||||
'warn',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-empty': ['error', { "allowEmptyCatch": true }],
|
||||
'no-restricted-properties': [
|
||||
'error',
|
||||
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
||||
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
||||
],
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
selector: 'Literal[value=/•/], JSXText[value=/•/]',
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
message: "Use '·' (middle dot) instead of '•' (bullet)",
|
||||
},
|
||||
],
|
||||
'no-unused-expressions': 'error',
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
vars: 'all',
|
||||
args: 'after-used',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
'valid-typeof': 'error',
|
||||
|
||||
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
|
||||
'react/jsx-boolean-value': 'error',
|
||||
'react/display-name': 'off',
|
||||
'react/jsx-fragments': ['error', 'syntax'],
|
||||
'react/jsx-equals-spacing': 'error',
|
||||
'react/jsx-no-bind': 'error',
|
||||
'react/jsx-no-useless-fragment': 'error',
|
||||
'react/jsx-no-target-blank': 'off',
|
||||
'react/jsx-tag-spacing': 'error',
|
||||
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
||||
'react/jsx-wrap-multilines': 'error',
|
||||
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
||||
'react/self-closing-comp': 'error',
|
||||
|
||||
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46
|
||||
'jsx-a11y/click-events-have-key-events': 'off',
|
||||
'jsx-a11y/label-has-associated-control': 'off',
|
||||
'jsx-a11y/media-has-caption': 'off',
|
||||
'jsx-a11y/no-autofocus': 'off',
|
||||
// recommended rule is:
|
||||
// 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
|
||||
// 'error',
|
||||
// {
|
||||
// tr: ['none', 'presentation'],
|
||||
// canvas: ['img'],
|
||||
// },
|
||||
// ],
|
||||
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
|
||||
// recommended rule is:
|
||||
// 'jsx-a11y/no-noninteractive-tabindex': [
|
||||
// 'error',
|
||||
// {
|
||||
// tags: [],
|
||||
// roles: ['tabpanel'],
|
||||
// allowExpressionValues: true,
|
||||
// },
|
||||
// ],
|
||||
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
||||
// recommended is full 'error'
|
||||
'jsx-a11y/no-static-element-interactions': [
|
||||
'warn',
|
||||
{
|
||||
handlers: [
|
||||
'onClick',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
// See https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/config/recommended.js
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
mjs: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
'import/first': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-anonymous-default-export': 'error',
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: [
|
||||
'.eslintrc.js',
|
||||
'config/webpack/**',
|
||||
'app/javascript/mastodon/performance.js',
|
||||
'app/javascript/mastodon/test_setup.js',
|
||||
'app/javascript/**/__tests__/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
'import/no-amd': 'error',
|
||||
'import/no-commonjs': 'error',
|
||||
'import/no-import-module-exports': 'error',
|
||||
'import/no-relative-packages': 'error',
|
||||
'import/no-self-import': 'error',
|
||||
'import/no-useless-path-segments': 'error',
|
||||
'import/no-webpack-loader-syntax': 'error',
|
||||
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
alphabetize: { order: 'asc' },
|
||||
'newlines-between': 'always',
|
||||
groups: [
|
||||
'builtin',
|
||||
'external',
|
||||
'internal',
|
||||
'parent',
|
||||
['index', 'sibling'],
|
||||
'object',
|
||||
],
|
||||
pathGroups: [
|
||||
// React core packages
|
||||
{
|
||||
pattern: '{react,react-dom,react-dom/client,prop-types}',
|
||||
group: 'builtin',
|
||||
position: 'after',
|
||||
},
|
||||
// I18n
|
||||
{
|
||||
pattern: '{react-intl,intl-messageformat}',
|
||||
group: 'builtin',
|
||||
position: 'after',
|
||||
},
|
||||
// Common React utilities
|
||||
{
|
||||
pattern: '{classnames,react-helmet,react-router,react-router-dom}',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
// Immutable / Redux / data store
|
||||
{
|
||||
pattern: '{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
// Internal packages
|
||||
{
|
||||
pattern: '{mastodon/**}',
|
||||
group: 'internal',
|
||||
position: 'after',
|
||||
},
|
||||
],
|
||||
pathGroupsExcludedImportTypes: [],
|
||||
},
|
||||
],
|
||||
|
||||
'promise/always-return': 'off',
|
||||
'promise/catch-or-return': [
|
||||
'error',
|
||||
{
|
||||
allowFinally: true,
|
||||
},
|
||||
],
|
||||
'promise/no-callback-in-promise': 'off',
|
||||
'promise/no-nesting': 'off',
|
||||
'promise/no-promise-in-callback': 'off',
|
||||
|
||||
'formatjs/blocklist-elements': 'error',
|
||||
'formatjs/enforce-default-message': ['error', 'literal'],
|
||||
'formatjs/enforce-description': 'off', // description values not currently used
|
||||
'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
|
||||
'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
|
||||
'formatjs/enforce-plural-rules': 'error',
|
||||
'formatjs/no-camel-case': 'off', // disabledAccount is only non-conforming
|
||||
'formatjs/no-complex-selectors': 'error',
|
||||
'formatjs/no-emoji': 'error',
|
||||
'formatjs/no-id': 'off', // IDs are used for translation keys
|
||||
'formatjs/no-invalid-icu': 'error',
|
||||
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
|
||||
'formatjs/no-multiple-whitespaces': 'error',
|
||||
'formatjs/no-offset': 'error',
|
||||
'formatjs/no-useless-message': 'error',
|
||||
'formatjs/prefer-formatted-message': 'error',
|
||||
'formatjs/prefer-pound-in-plural': 'error',
|
||||
|
||||
'jsdoc/check-types': 'off',
|
||||
'jsdoc/no-undefined-types': 'off',
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'jsdoc/require-param-description': 'off',
|
||||
'jsdoc/require-property-description': 'off',
|
||||
'jsdoc/require-returns-description': 'off',
|
||||
'jsdoc/require-returns': 'off',
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'.eslintrc.js',
|
||||
'*.config.js',
|
||||
'.*rc.js',
|
||||
'ide-helper.js',
|
||||
'config/webpack/**/*',
|
||||
'config/formatjs-formatter.js',
|
||||
],
|
||||
|
||||
env: {
|
||||
commonjs: true,
|
||||
},
|
||||
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
},
|
||||
|
||||
rules: {
|
||||
'import/no-commonjs': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.ts',
|
||||
'**/*.tsx',
|
||||
],
|
||||
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/strict-type-checked',
|
||||
'plugin:@typescript-eslint/stylistic-type-checked',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
'plugin:promise/recommended',
|
||||
'plugin:jsdoc/recommended-typescript',
|
||||
],
|
||||
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
|
||||
rules: {
|
||||
// Disable formatting rules that have been enabled in the base config
|
||||
'indent': 'off',
|
||||
|
||||
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
|
||||
'consistent-return': 'off',
|
||||
|
||||
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
'@typescript-eslint/consistent-type-exports': 'error',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
"@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }],
|
||||
"@typescript-eslint/no-restricted-imports": [
|
||||
"warn",
|
||||
{
|
||||
"name": "react-redux",
|
||||
"importNames": ["useSelector", "useDispatch"],
|
||||
"message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead."
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/restrict-template-expressions": ['warn', { allowNumber: true }],
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
|
||||
// Those rules set stricter rules for TS files
|
||||
// to enforce better practices when converting from JS
|
||||
'import/no-default-export': 'warn',
|
||||
'react/prefer-stateless-function': 'warn',
|
||||
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
|
||||
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
||||
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/__tests__/*.js',
|
||||
'**/__tests__/*.jsx',
|
||||
],
|
||||
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
1
.github/.well-known/funding-manifest-urls
vendored
Normal file
1
.github/.well-known/funding-manifest-urls
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
https://joinmastodon.org/funding.json
|
13
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/1.web_bug_report.yml
vendored
|
@ -1,6 +1,7 @@
|
|||
name: Bug Report (Web Interface)
|
||||
description: If you are using Mastodon's web interface and something is not working as expected
|
||||
labels: [bug, 'status/to triage', 'area/web interface']
|
||||
description: There is a problem using Mastodon's web interface.
|
||||
labels: ['status/to triage', 'area/web interface']
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
@ -47,8 +48,8 @@ body:
|
|||
attributes:
|
||||
label: Mastodon version
|
||||
description: |
|
||||
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
||||
placeholder: v4.1.2
|
||||
This is displayed at the bottom of the About page, eg. `v4.4.0-beta.1`
|
||||
placeholder: v4.4.0-beta.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
@ -56,7 +57,7 @@ body:
|
|||
label: Browser name and version
|
||||
description: |
|
||||
What browser are you using when getting this bug? Please specify the version as well.
|
||||
placeholder: Firefox 105.0.3
|
||||
placeholder: Firefox 139.0.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
@ -64,7 +65,7 @@ body:
|
|||
label: Operating system
|
||||
description: |
|
||||
What OS are you running? Please specify the version as well.
|
||||
placeholder: macOS 13.4.1
|
||||
placeholder: macOS 15.5
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
|
13
.github/ISSUE_TEMPLATE/2.server_bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/2.server_bug_report.yml
vendored
|
@ -1,7 +1,8 @@
|
|||
name: Bug Report (server / API)
|
||||
description: |
|
||||
If something is not working as expected, but is not from using the web interface.
|
||||
labels: [bug, 'status/to triage']
|
||||
There is a problem with the HTTP server, REST API, ActivityPub interaction, etc.
|
||||
labels: ['status/to triage']
|
||||
type: 'Bug'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
@ -48,8 +49,8 @@ body:
|
|||
attributes:
|
||||
label: Mastodon version
|
||||
description: |
|
||||
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
|
||||
placeholder: v4.1.2
|
||||
This is displayed at the bottom of the About page, eg. `v4.4.0-beta.1`
|
||||
placeholder: v4.4.0-beta.1
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
|
@ -59,7 +60,7 @@ body:
|
|||
Any additional technical details you may have, like logs or error traces
|
||||
value: |
|
||||
If this is happening on your own Mastodon server, please fill out those:
|
||||
- Ruby version: (from `ruby --version`, eg. v3.1.2)
|
||||
- Node.js version: (from `node --version`, eg. v18.16.0)
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.4)
|
||||
- Node.js version: (from `node --version`, eg. v22.16.0)
|
||||
validations:
|
||||
required: false
|
||||
|
|
74
.github/ISSUE_TEMPLATE/3.troubleshooting.yml
vendored
Normal file
74
.github/ISSUE_TEMPLATE/3.troubleshooting.yml
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
name: Deployment troubleshooting
|
||||
description: |
|
||||
You are a server administrator and you are encountering a technical issue during installation, upgrade or operations of Mastodon.
|
||||
labels: ['status/to triage']
|
||||
type: 'Troubleshooting'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Make sure that you are submitting a new bug that was not previously reported or already fixed.
|
||||
|
||||
Please use a concise and distinct title for the issue.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce the problem
|
||||
description: What were you trying to do?
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: What should have happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Actual behaviour
|
||||
description: What happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Detailed description
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mastodon instance
|
||||
description: The address of the Mastodon instance where you experienced the issue
|
||||
placeholder: mastodon.social
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Mastodon version
|
||||
description: |
|
||||
This is displayed at the bottom of the About page, eg. `v4.4.0-alpha.1`
|
||||
placeholder: v4.4.0-beta.1
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
Details about your environment, like how Mastodon is deployed, if containers are used, version numbers, etc.
|
||||
value: |
|
||||
Please at least include those informations:
|
||||
- Operating system: (eg. Ubuntu 24.04.2)
|
||||
- Ruby version: (from `ruby --version`, eg. v3.4.4)
|
||||
- Node.js version: (from `node --version`, eg. v22.16.0)
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Technical details
|
||||
description: |
|
||||
Any additional technical details you may have, like logs or error traces
|
||||
validations:
|
||||
required: false
|
|
@ -1,6 +1,6 @@
|
|||
name: Feature Request
|
||||
description: I have a suggestion
|
||||
labels: [suggestion]
|
||||
type: Suggestion
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
1
.github/actions/setup-ruby/action.yml
vendored
1
.github/actions/setup-ruby/action.yml
vendored
|
@ -21,4 +21,3 @@ runs:
|
|||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
bundler-cache: true
|
||||
cache-version: 4.3
|
||||
|
|
31
.github/renovate.json5
vendored
31
.github/renovate.json5
vendored
|
@ -15,6 +15,8 @@
|
|||
// to `null` after any other rule set it to something.
|
||||
dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).',
|
||||
postUpdateOptions: ['yarnDedupeHighest'],
|
||||
// The types are now included in recent versions,we ignore them here until we upgrade and remove the dependency
|
||||
ignoreDeps: ['@types/emoji-mart'],
|
||||
packageRules: [
|
||||
{
|
||||
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
||||
|
@ -23,26 +25,12 @@
|
|||
'tesseract.js', // Requires code changes
|
||||
'react-hotkeys', // Requires code changes
|
||||
|
||||
// Requires Webpacker upgrade or replacement
|
||||
'@svgr/webpack',
|
||||
'@types/webpack',
|
||||
'babel-loader',
|
||||
'compression-webpack-plugin',
|
||||
'css-loader',
|
||||
'imports-loader',
|
||||
'mini-css-extract-plugin',
|
||||
'postcss-loader',
|
||||
'sass-loader',
|
||||
'terser-webpack-plugin',
|
||||
'webpack',
|
||||
'webpack-assets-manifest',
|
||||
'webpack-bundle-analyzer',
|
||||
'webpack-dev-server',
|
||||
'webpack-cli',
|
||||
|
||||
// react-router: Requires manual upgrade
|
||||
'history',
|
||||
'react-router-dom',
|
||||
|
||||
// react-spring: Requires manual upgrade when upgrading react
|
||||
'@react-spring/web',
|
||||
],
|
||||
matchUpdateTypes: ['major'],
|
||||
dependencyDashboardApproval: true,
|
||||
|
@ -51,7 +39,6 @@
|
|||
// Require Dependency Dashboard Approval for major version bumps of these Ruby packages
|
||||
matchManagers: ['bundler'],
|
||||
matchPackageNames: [
|
||||
'rack', // Needs to be synced with Rails version
|
||||
'strong_migrations', // Requires manual upgrade
|
||||
'sidekiq', // Requires manual upgrade
|
||||
'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version
|
||||
|
@ -97,7 +84,13 @@
|
|||
{
|
||||
// Group all eslint-related packages with `eslint` in the same PR
|
||||
matchManagers: ['npm'],
|
||||
matchPackageNames: ['eslint', 'eslint-*', '@typescript-eslint/*'],
|
||||
matchPackageNames: [
|
||||
'eslint',
|
||||
'eslint-*',
|
||||
'typescript-eslint',
|
||||
'@eslint/*',
|
||||
'globals',
|
||||
],
|
||||
matchUpdateTypes: ['patch', 'minor'],
|
||||
groupName: 'eslint (non-major)',
|
||||
},
|
||||
|
|
1
.github/workflows/build-releases.yml
vendored
1
.github/workflows/build-releases.yml
vendored
|
@ -28,7 +28,6 @@ jobs:
|
|||
secrets: inherit
|
||||
|
||||
build-image-streaming:
|
||||
if: startsWith(github.ref, 'refs/tags/v4.3.')
|
||||
uses: ./.github/workflows/build-container-image.yml
|
||||
with:
|
||||
file_to_build: streaming/Dockerfile
|
||||
|
|
2
.github/workflows/bundler-audit.yml
vendored
2
.github/workflows/bundler-audit.yml
vendored
|
@ -36,4 +36,4 @@ jobs:
|
|||
bundler-cache: true
|
||||
|
||||
- name: Run bundler-audit
|
||||
run: bundle exec bundler-audit check --update
|
||||
run: bin/bundler-audit check --update
|
||||
|
|
12
.github/workflows/check-i18n.yml
vendored
12
.github/workflows/check-i18n.yml
vendored
|
@ -18,7 +18,7 @@ permissions:
|
|||
|
||||
jobs:
|
||||
check-i18n:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -35,18 +35,18 @@ jobs:
|
|||
git diff --exit-code
|
||||
|
||||
- name: Check locale file normalization
|
||||
run: bundle exec i18n-tasks check-normalized
|
||||
run: bin/i18n-tasks check-normalized
|
||||
|
||||
- name: Check for unused strings
|
||||
run: bundle exec i18n-tasks unused
|
||||
run: bin/i18n-tasks unused
|
||||
|
||||
- name: Check for missing strings in English YML
|
||||
run: |
|
||||
bundle exec i18n-tasks add-missing -l en
|
||||
bin/i18n-tasks add-missing -l en
|
||||
git diff --exit-code
|
||||
|
||||
- name: Check for wrong string interpolations
|
||||
run: bundle exec i18n-tasks check-consistent-interpolations
|
||||
run: bin/i18n-tasks check-consistent-interpolations
|
||||
|
||||
- name: Check that all required locale files exist
|
||||
run: bundle exec rake repo:check_locales_files
|
||||
run: bin/rake repo:check_locales_files
|
||||
|
|
41
.github/workflows/chromatic.yml
vendored
Normal file
41
.github/workflows/chromatic.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
name: 'Chromatic'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- renovate/*
|
||||
- stable-*
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'yarn.lock'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
- '**/*.ts'
|
||||
- '**/*.tsx'
|
||||
- '**/*.css'
|
||||
- '**/*.scss'
|
||||
- '.github/workflows/chromatic.yml'
|
||||
|
||||
jobs:
|
||||
chromatic:
|
||||
name: Run Chromatic
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'mastodon/mastodon'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Build Storybook
|
||||
run: yarn build-storybook
|
||||
|
||||
- name: Run Chromatic
|
||||
uses: chromaui/action@v12
|
||||
with:
|
||||
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
zip: true
|
||||
storybookBuildDir: 'storybook-static'
|
|
@ -46,11 +46,11 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Run i18n normalize task
|
||||
run: bundle exec i18n-tasks normalize
|
||||
run: bin/i18n-tasks normalize
|
||||
|
||||
# Create or update the pull request
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.5
|
||||
uses: peter-evans/create-pull-request@v7.0.6
|
||||
with:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations for ${{ github.base_ref || github.ref_name }} (automated)'
|
||||
|
|
4
.github/workflows/crowdin-download.yml
vendored
4
.github/workflows/crowdin-download.yml
vendored
|
@ -48,11 +48,11 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Run i18n normalize task
|
||||
run: bundle exec i18n-tasks normalize
|
||||
run: bin/i18n-tasks normalize
|
||||
|
||||
# Create or update the pull request
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.5
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
commit-message: 'New Crowdin translations'
|
||||
title: 'New Crowdin Translations (automated)'
|
||||
|
|
1
.github/workflows/crowdin-upload.yml
vendored
1
.github/workflows/crowdin-upload.yml
vendored
|
@ -14,6 +14,7 @@ on:
|
|||
- config/locales/devise.en.yml
|
||||
- config/locales/doorkeeper.en.yml
|
||||
- .github/workflows/crowdin-upload.yml
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
upload-translations:
|
||||
|
|
2
.github/workflows/lint-css.yml
vendored
2
.github/workflows/lint-css.yml
vendored
|
@ -40,4 +40,4 @@ jobs:
|
|||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Stylelint
|
||||
run: yarn lint:css -f github
|
||||
run: yarn lint:css --custom-formatter @csstools/stylelint-formatter-github
|
||||
|
|
2
.github/workflows/lint-haml.yml
vendored
2
.github/workflows/lint-haml.yml
vendored
|
@ -43,4 +43,4 @@ jobs:
|
|||
- name: Run haml-lint
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
||||
bundle exec haml-lint --reporter github
|
||||
bin/haml-lint --reporter github
|
||||
|
|
6
.github/workflows/lint-js.yml
vendored
6
.github/workflows/lint-js.yml
vendored
|
@ -11,7 +11,7 @@ on:
|
|||
- 'tsconfig.json'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- '.eslint*'
|
||||
- 'eslint.config.mjs'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
- '**/*.ts'
|
||||
|
@ -25,7 +25,7 @@ on:
|
|||
- 'tsconfig.json'
|
||||
- '.nvmrc'
|
||||
- '.prettier*'
|
||||
- '.eslint*'
|
||||
- 'eslint.config.mjs'
|
||||
- '**/*.js'
|
||||
- '**/*.jsx'
|
||||
- '**/*.ts'
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: ESLint
|
||||
run: yarn lint:js --max-warnings 0
|
||||
run: yarn workspaces foreach --all --parallel run lint:js --max-warnings 0
|
||||
|
||||
- name: Typecheck
|
||||
run: yarn typecheck
|
||||
|
|
2
.github/workflows/lint-ruby.yml
vendored
2
.github/workflows/lint-ruby.yml
vendored
|
@ -9,6 +9,7 @@ on:
|
|||
- 'Gemfile*'
|
||||
- '.rubocop*.yml'
|
||||
- '.ruby-version'
|
||||
- 'bin/rubocop'
|
||||
- 'config/brakeman.ignore'
|
||||
- '**/*.rb'
|
||||
- '**/*.rake'
|
||||
|
@ -19,6 +20,7 @@ on:
|
|||
- 'Gemfile*'
|
||||
- '.rubocop*.yml'
|
||||
- '.ruby-version'
|
||||
- 'bin/rubocop'
|
||||
- 'config/brakeman.ignore'
|
||||
- '**/*.rb'
|
||||
- '**/*.rake'
|
||||
|
|
1
.github/workflows/test-image-build.yml
vendored
1
.github/workflows/test-image-build.yml
vendored
|
@ -8,6 +8,7 @@ on:
|
|||
- .github/workflows/test-image-build.yml
|
||||
- Dockerfile
|
||||
- streaming/Dockerfile
|
||||
- .dockerignore
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
|
2
.github/workflows/test-js.yml
vendored
2
.github/workflows/test-js.yml
vendored
|
@ -40,4 +40,4 @@ jobs:
|
|||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: JavaScript testing
|
||||
run: yarn jest --reporters github-actions summary
|
||||
run: yarn test:js
|
||||
|
|
19
.github/workflows/test-migrations.yml
vendored
19
.github/workflows/test-migrations.yml
vendored
|
@ -12,6 +12,7 @@ on:
|
|||
- '**/*.rb'
|
||||
- '.github/workflows/test-migrations.yml'
|
||||
- 'lib/tasks/tests.rake'
|
||||
- 'lib/tasks/db.rake'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
|
@ -63,7 +64,6 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: true
|
||||
RAILS_ENV: test
|
||||
BUNDLE_CLEAN: true
|
||||
BUNDLE_FROZEN: true
|
||||
|
@ -77,6 +77,18 @@ jobs:
|
|||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Ensure no errors with `db:prepare`
|
||||
run: |
|
||||
bin/rails db:drop
|
||||
bin/rails db:prepare
|
||||
bin/rails db:migrate
|
||||
|
||||
- name: Ensure no errors with `db:prepare` and SKIP_POST_DEPLOYMENT_MIGRATIONS
|
||||
run: |
|
||||
bin/rails db:drop
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:prepare
|
||||
bin/rails db:migrate
|
||||
|
||||
- name: Test "one step migration" flow
|
||||
run: |
|
||||
bin/rails db:drop
|
||||
|
@ -90,6 +102,11 @@ jobs:
|
|||
bin/rails db:drop
|
||||
bin/rails db:create
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
|
||||
|
||||
# Migrate up to v4.2.0 breakpoint
|
||||
bin/rails db:migrate VERSION=20230907150100
|
||||
|
||||
# Migrate the rest
|
||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
|
||||
bin/rails db:migrate
|
||||
bin/rails tests:migrations:check_database
|
||||
|
|
59
.github/workflows/test-ruby.yml
vendored
59
.github/workflows/test-ruby.yml
vendored
|
@ -49,7 +49,7 @@ jobs:
|
|||
public/assets
|
||||
public/packs
|
||||
public/packs-test
|
||||
tmp/cache/webpacker
|
||||
tmp/cache/vite
|
||||
key: ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
|
||||
- name: Archive asset artifacts
|
||||
run: |
|
||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
|
||||
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tmp/cache/vite/last-build*.json
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.mode == 'test'
|
||||
|
@ -107,7 +107,7 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||
COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
|
||||
RAILS_ENV: test
|
||||
ALLOW_NOPAM: true
|
||||
PAM_ENABLED: true
|
||||
|
@ -124,10 +124,9 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.1'
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '.ruby-version'
|
||||
- '3.4'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
@ -144,7 +143,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||
additional-system-dependencies: ffmpeg libpam-dev
|
||||
|
||||
- name: Load database schema
|
||||
run: |
|
||||
|
@ -168,15 +167,15 @@ jobs:
|
|||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.ruby-version == '.ruby-version'
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: coverage/lcov/*.lcov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-libvips:
|
||||
name: Libvips tests
|
||||
runs-on: ubuntu-24.04
|
||||
test-imagemagick:
|
||||
name: ImageMagick tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
@ -209,7 +208,7 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||
COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
|
||||
RAILS_ENV: test
|
||||
ALLOW_NOPAM: true
|
||||
PAM_ENABLED: true
|
||||
|
@ -221,16 +220,15 @@ jobs:
|
|||
CAS_ENABLED: true
|
||||
BUNDLE_WITH: 'pam_authentication test'
|
||||
GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
|
||||
MASTODON_USE_LIBVIPS: true
|
||||
MASTODON_USE_LIBVIPS: false
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.1'
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '.ruby-version'
|
||||
- '3.4'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
@ -247,7 +245,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
additional-system-dependencies: ffmpeg libpam-dev
|
||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||
|
||||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
@ -256,7 +254,7 @@ jobs:
|
|||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.ruby-version == '.ruby-version'
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: coverage/lcov/mastodon.lcov
|
||||
env:
|
||||
|
@ -297,7 +295,6 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: true
|
||||
RAILS_ENV: test
|
||||
BUNDLE_WITH: test
|
||||
LOCAL_DOMAIN: localhost:3000
|
||||
|
@ -307,10 +304,9 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.1'
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '.ruby-version'
|
||||
- '3.4'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -328,7 +324,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
additional-system-dependencies: ffmpeg imagemagick
|
||||
additional-system-dependencies: ffmpeg
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
@ -336,6 +332,21 @@ jobs:
|
|||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
||||
- name: Cache Playwright Chromium browser
|
||||
id: playwright-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
- name: Install Playwright Chromium browser (with deps)
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: yarn run playwright install --with-deps chromium
|
||||
|
||||
- name: Install Playwright Chromium browser deps
|
||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
||||
run: yarn run playwright install-deps chromium
|
||||
|
||||
- run: bin/rspec spec/system --tag streaming --tag js
|
||||
|
||||
- name: Archive logs
|
||||
|
@ -349,7 +360,7 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: e2e-screenshots
|
||||
name: e2e-screenshots-${{ matrix.ruby-version }}
|
||||
path: tmp/capybara/
|
||||
|
||||
test-search:
|
||||
|
@ -414,7 +425,6 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: true
|
||||
RAILS_ENV: test
|
||||
BUNDLE_WITH: test
|
||||
ES_ENABLED: true
|
||||
|
@ -425,10 +435,9 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
ruby-version:
|
||||
- '3.1'
|
||||
- '3.2'
|
||||
- '3.3'
|
||||
- '.ruby-version'
|
||||
- '3.4'
|
||||
search-image:
|
||||
- docker.elastic.co/elasticsearch/elasticsearch:7.17.13
|
||||
include:
|
||||
|
@ -449,7 +458,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
additional-system-dependencies: ffmpeg imagemagick
|
||||
additional-system-dependencies: ffmpeg
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -21,10 +21,11 @@
|
|||
/public/system
|
||||
/public/assets
|
||||
/public/packs
|
||||
/public/packs-dev
|
||||
/public/packs-test
|
||||
.env
|
||||
.env.production
|
||||
/node_modules/
|
||||
node_modules/
|
||||
/build/
|
||||
|
||||
# Ignore Vagrant files
|
||||
|
@ -74,3 +75,6 @@ docker-compose.override.yml
|
|||
|
||||
# Ignore local-only rspec configuration
|
||||
.rspec-local
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
!/log/.keep
|
||||
/tmp
|
||||
/coverage
|
||||
/public/system
|
||||
/public/assets
|
||||
/public/packs
|
||||
/public/packs-test
|
||||
.env
|
||||
.env.production
|
||||
.env.development
|
||||
|
@ -60,9 +56,11 @@ docker-compose.override.yml
|
|||
/public/packs
|
||||
/public/packs-test
|
||||
/public/system
|
||||
/public/vite*
|
||||
|
||||
# Ignore emoji map file
|
||||
/app/javascript/mastodon/features/emoji/emoji_map.json
|
||||
/app/javascript/mastodon/features/emoji/emoji_data.json
|
||||
|
||||
# Ignore locale files
|
||||
/app/javascript/mastodon/locales/*.json
|
||||
|
@ -83,3 +81,6 @@ AUTHORS.md
|
|||
|
||||
# Process a few selected JS files
|
||||
!lint-staged.config.js
|
||||
|
||||
# Ignore config YAML files that include ERB/ruby code prettier does not understand
|
||||
/config/email.yml
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true
|
||||
}
|
||||
};
|
||||
|
|
10
.rubocop.yml
10
.rubocop.yml
|
@ -8,7 +8,7 @@ AllCops:
|
|||
- lib/mastodon/migration_helpers.rb
|
||||
ExtraDetails: true
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.1 # Oldest supported ruby version
|
||||
TargetRubyVersion: 3.2 # Oldest supported ruby version
|
||||
|
||||
inherit_from:
|
||||
- .rubocop/layout.yml
|
||||
|
@ -18,6 +18,7 @@ inherit_from:
|
|||
- .rubocop/rspec_rails.yml
|
||||
- .rubocop/rspec.yml
|
||||
- .rubocop/style.yml
|
||||
- .rubocop/i18n.yml
|
||||
- .rubocop/custom.yml
|
||||
- .rubocop_todo.yml
|
||||
- .rubocop/strict.yml
|
||||
|
@ -26,9 +27,10 @@ inherit_mode:
|
|||
merge:
|
||||
- Exclude
|
||||
|
||||
require:
|
||||
plugins:
|
||||
- rubocop-capybara
|
||||
- rubocop-i18n
|
||||
- rubocop-performance
|
||||
- rubocop-rails
|
||||
- rubocop-rspec
|
||||
- rubocop-rspec_rails
|
||||
- rubocop-performance
|
||||
- rubocop-capybara
|
||||
|
|
12
.rubocop/i18n.yml
Normal file
12
.rubocop/i18n.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
I18n/RailsI18n:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'config/**/*'
|
||||
- 'db/**/*'
|
||||
- 'lib/**/*'
|
||||
- 'spec/**/*'
|
||||
I18n/GetText:
|
||||
Enabled: false
|
||||
|
||||
I18n/RailsI18n/DecorateStringFormattingUsingInterpolation:
|
||||
Enabled: false
|
|
@ -1,3 +1,6 @@
|
|||
---
|
||||
Naming/BlockForwarding:
|
||||
EnforcedStyle: explicit
|
||||
|
||||
Naming/PredicateMethod:
|
||||
Enabled: false
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Rails/BulkChangeTable:
|
||||
Enabled: false # Conflicts with strong_migrations features
|
||||
|
||||
Rails/Delegate:
|
||||
Enabled: false
|
||||
|
||||
Rails/FilePath:
|
||||
EnforcedStyle: arguments
|
||||
|
||||
|
|
|
@ -23,5 +23,6 @@ RSpec/SpecFilePathFormat:
|
|||
ActivityPub: activitypub
|
||||
DeepL: deepl
|
||||
FetchOEmbedService: fetch_oembed_service
|
||||
OAuth: oauth
|
||||
OEmbedController: oembed_controller
|
||||
OStatus: ostatus
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
Style/ArrayIntersect:
|
||||
Enabled: false
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
|
@ -19,6 +22,13 @@ Style/HashSyntax:
|
|||
EnforcedShorthandSyntax: either
|
||||
EnforcedStyle: ruby19_no_mixed_keys
|
||||
|
||||
Style/IfUnlessModifier:
|
||||
Exclude:
|
||||
- '**/*.haml'
|
||||
|
||||
Style/KeywordArgumentsMerging:
|
||||
Enabled: false
|
||||
|
||||
Style/NumericLiterals:
|
||||
AllowedPatterns:
|
||||
- \d{4}_\d{2}_\d{2}_\d{6}
|
||||
|
@ -37,6 +47,9 @@ Style/RedundantFetchBlock:
|
|||
Style/RescueStandardError:
|
||||
EnforcedStyle: implicit
|
||||
|
||||
Style/SafeNavigationChainLength:
|
||||
Enabled: false
|
||||
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
|
@ -45,3 +58,6 @@ Style/TrailingCommaInArrayLiteral:
|
|||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/WordArray:
|
||||
MinSize: 3 # Override default of 2
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.66.1.
|
||||
# using RuboCop version 1.77.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
|
@ -8,7 +8,7 @@
|
|||
|
||||
Lint/NonLocalExitFromIterator:
|
||||
Exclude:
|
||||
- 'app/helpers/jsonld_helper.rb'
|
||||
- 'app/helpers/json_ld_helper.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
|
@ -27,87 +27,13 @@ Metrics/CyclomaticComplexity:
|
|||
Metrics/PerceivedComplexity:
|
||||
Max: 27
|
||||
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'config/initializers/simple_form.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowedVars.
|
||||
# Configuration parameters: AllowedVars, DefaultToNil.
|
||||
Style/FetchEnvVar:
|
||||
Exclude:
|
||||
- 'app/lib/translation_service.rb'
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/2_limited_federation_mode.rb'
|
||||
- 'config/initializers/3_omniauth.rb'
|
||||
- 'config/initializers/cache_buster.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'config/initializers/paperclip.rb'
|
||||
- 'config/initializers/vapid.rb'
|
||||
- 'lib/tasks/repo.rake'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
|
||||
# SupportedStyles: annotated, template, unannotated
|
||||
# AllowedMethods: redirect
|
||||
Style/FormatStringToken:
|
||||
Exclude:
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'lib/paperclip/color_extractor.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/HashTransformValues:
|
||||
Exclude:
|
||||
- 'app/serializers/rest/web_push_subscription_serializer.rb'
|
||||
- 'app/services/import_service.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/MapToHash:
|
||||
Exclude:
|
||||
- 'app/models/status.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: literals, strict
|
||||
Style/MutableConstant:
|
||||
Exclude:
|
||||
- 'app/models/tag.rb'
|
||||
- 'app/services/delete_account_service.rb'
|
||||
- 'lib/mastodon/migration_warning.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods.
|
||||
# AllowedMethods: respond_to_missing?
|
||||
Style/OptionalBooleanParameter:
|
||||
Exclude:
|
||||
- 'app/helpers/jsonld_helper.rb'
|
||||
- 'app/lib/admin/system_check/message.rb'
|
||||
- 'app/lib/request.rb'
|
||||
- 'app/lib/webfinger.rb'
|
||||
- 'app/services/block_domain_service.rb'
|
||||
- 'app/services/fetch_resource_service.rb'
|
||||
- 'app/workers/domain_block_worker.rb'
|
||||
- 'app/workers/unfollow_follow_worker.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: short, verbose
|
||||
Style/PreferredHashMethods:
|
||||
Exclude:
|
||||
- 'config/initializers/paperclip.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/RedundantConstantBase:
|
||||
Exclude:
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/sidekiq.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: WordRegex.
|
||||
# SupportedStyles: percent, brackets
|
||||
Style/WordArray:
|
||||
EnforcedStyle: percent
|
||||
MinSize: 3
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.3.5
|
||||
3.4.4
|
||||
|
|
31
.storybook/main.ts
Normal file
31
.storybook/main.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../app/javascript/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-a11y',
|
||||
'@storybook/addon-vitest',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
staticDirs: [
|
||||
'./static',
|
||||
// We need to manually specify the assets because of the symlink in public/sw.js
|
||||
...[
|
||||
'avatars',
|
||||
'emoji',
|
||||
'headers',
|
||||
'sounds',
|
||||
'badge.png',
|
||||
'loading.gif',
|
||||
'loading.png',
|
||||
'oops.gif',
|
||||
'oops.png',
|
||||
].map((path) => ({ from: `../public/${path}`, to: `/${path}` })),
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
7
.storybook/manager.ts
Normal file
7
.storybook/manager.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { addons } from 'storybook/manager-api';
|
||||
|
||||
import theme from './storybook-theme';
|
||||
|
||||
addons.setConfig({
|
||||
theme,
|
||||
});
|
18
.storybook/preview-head.html
Normal file
18
.storybook/preview-head.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<style>
|
||||
/* Increase docs font size */
|
||||
.sbdocs.sbdocs-content :where(p:not(.sb-anchor, .sb-unstyled, .sb-unstyled p)),
|
||||
.sbdocs.sbdocs-content :where(li:not(.sb-anchor, .sb-unstyled, .sb-unstyled li)) {
|
||||
font-size: 1.0666rem; /* 17px */
|
||||
line-height: 1.585; /* 27px */
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-content :where(p:not(.sb-anchor, .sb-unstyled, .sb-unstyled p)) code,
|
||||
.sbdocs.sbdocs-content :where(li:not(.sb-anchor, .sb-unstyled, .sb-unstyled li)) code {
|
||||
font-size: 0.875rem; /* ~15px */
|
||||
}
|
||||
|
||||
/* Bring numbers back for ordered lists */
|
||||
ol {
|
||||
list-style: revert !important;
|
||||
}
|
||||
</style>
|
146
.storybook/preview.tsx
Normal file
146
.storybook/preview.tsx
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import { MemoryRouter, Route } from 'react-router';
|
||||
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import type { Preview } from '@storybook/react-vite';
|
||||
import { initialize, mswLoader } from 'msw-storybook-addon';
|
||||
import { action } from 'storybook/actions';
|
||||
|
||||
import type { LocaleData } from '@/mastodon/locales';
|
||||
import { reducerWithInitialState, rootReducer } from '@/mastodon/reducers';
|
||||
import { defaultMiddleware } from '@/mastodon/store/store';
|
||||
import { mockHandlers, unhandledRequestHandler } from '@/testing/api';
|
||||
|
||||
// If you want to run the dark theme during development,
|
||||
// you can change the below to `/application.scss`
|
||||
import '../app/javascript/styles/mastodon-light.scss';
|
||||
|
||||
const localeFiles = import.meta.glob('@/mastodon/locales/*.json', {
|
||||
query: { as: 'json' },
|
||||
});
|
||||
|
||||
// Initialize MSW
|
||||
initialize({
|
||||
onUnhandledRequest: unhandledRequestHandler,
|
||||
});
|
||||
|
||||
const preview: Preview = {
|
||||
// Auto-generate docs: https://storybook.js.org/docs/writing-docs/autodocs
|
||||
tags: ['autodocs'],
|
||||
globalTypes: {
|
||||
locale: {
|
||||
description: 'Locale for the story',
|
||||
toolbar: {
|
||||
title: 'Locale',
|
||||
icon: 'globe',
|
||||
items: Object.keys(localeFiles).map((path) =>
|
||||
path.replace('/mastodon/locales/', '').replace('.json', ''),
|
||||
),
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
initialGlobals: {
|
||||
locale: 'en',
|
||||
},
|
||||
decorators: [
|
||||
(Story, { parameters }) => {
|
||||
const { state = {} } = parameters;
|
||||
let reducer = rootReducer;
|
||||
if (typeof state === 'object' && state) {
|
||||
reducer = reducerWithInitialState(state as Record<string, unknown>);
|
||||
}
|
||||
const store = configureStore({
|
||||
reducer,
|
||||
middleware(getDefaultMiddleware) {
|
||||
return getDefaultMiddleware(defaultMiddleware);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Story />
|
||||
</Provider>
|
||||
);
|
||||
},
|
||||
(Story, { globals }) => {
|
||||
const currentLocale = (globals.locale as string) || 'en';
|
||||
const [messages, setMessages] = useState<
|
||||
Record<string, Record<string, string>>
|
||||
>({});
|
||||
const currentLocaleData = messages[currentLocale];
|
||||
|
||||
useEffect(() => {
|
||||
async function loadLocaleData() {
|
||||
const { default: localeFile } = (await import(
|
||||
`@/mastodon/locales/${currentLocale}.json`
|
||||
)) as { default: LocaleData['messages'] };
|
||||
setMessages((prevLocales) => ({
|
||||
...prevLocales,
|
||||
[currentLocale]: localeFile,
|
||||
}));
|
||||
}
|
||||
if (!currentLocaleData) {
|
||||
void loadLocaleData();
|
||||
}
|
||||
}, [currentLocale, currentLocaleData]);
|
||||
|
||||
return (
|
||||
<IntlProvider
|
||||
locale={currentLocale}
|
||||
messages={currentLocaleData}
|
||||
textComponent='span'
|
||||
>
|
||||
<Story />
|
||||
</IntlProvider>
|
||||
);
|
||||
},
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
<Route
|
||||
path='*'
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
render={({ location }) => {
|
||||
if (location.pathname !== '/') {
|
||||
action(`route change to ${location.pathname}`)(location);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
loaders: [mswLoader],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
|
||||
a11y: {
|
||||
// 'todo' - show a11y violations in the test UI only
|
||||
// 'error' - fail CI on a11y violations
|
||||
// 'off' - skip a11y checks entirely
|
||||
test: 'todo',
|
||||
},
|
||||
|
||||
state: {},
|
||||
|
||||
docs: {},
|
||||
|
||||
msw: {
|
||||
handlers: mockHandlers,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
344
.storybook/static/mockServiceWorker.js
Normal file
344
.storybook/static/mockServiceWorker.js
Normal file
|
@ -0,0 +1,344 @@
|
|||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
/**
|
||||
* Mock Service Worker.
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.10.2'
|
||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
addEventListener('install', function () {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
addEventListener('message', async function (event) {
|
||||
const clientId = Reflect.get(event.source || {}, 'id')
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'INTEGRITY_CHECK_RESPONSE',
|
||||
payload: {
|
||||
packageVersion: PACKAGE_VERSION,
|
||||
checksum: INTEGRITY_CHECKSUM,
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
payload: {
|
||||
client: {
|
||||
id: client.id,
|
||||
frameType: client.frameType,
|
||||
},
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_DEACTIVATE': {
|
||||
activeClientIds.delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
addEventListener('fetch', function (event) {
|
||||
// Bypass navigation requests.
|
||||
if (event.request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (
|
||||
event.request.cache === 'only-if-cached' &&
|
||||
event.request.mode !== 'same-origin'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been deleted (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = crypto.randomUUID()
|
||||
event.respondWith(handleRequest(event, requestId))
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {string} requestId
|
||||
*/
|
||||
async function handleRequest(event, requestId) {
|
||||
const client = await resolveMainClient(event)
|
||||
const requestCloneForEvents = event.request.clone()
|
||||
const response = await getResponse(event, client, requestId)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
||||
|
||||
// Clone the response so both the client and the library could consume it.
|
||||
const responseClone = response.clone()
|
||||
|
||||
sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
||||
request: {
|
||||
id: requestId,
|
||||
...serializedRequest,
|
||||
},
|
||||
response: {
|
||||
type: responseClone.type,
|
||||
status: responseClone.status,
|
||||
statusText: responseClone.statusText,
|
||||
headers: Object.fromEntries(responseClone.headers.entries()),
|
||||
body: responseClone.body,
|
||||
},
|
||||
},
|
||||
},
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the main client for the given event.
|
||||
* Client that issues a request doesn't necessarily equal the client
|
||||
* that registered the worker. It's with the latter the worker should
|
||||
* communicate with during the response resolving phase.
|
||||
* @param {FetchEvent} event
|
||||
* @returns {Promise<Client | undefined>}
|
||||
*/
|
||||
async function resolveMainClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (activeClientIds.has(event.clientId)) {
|
||||
return client
|
||||
}
|
||||
|
||||
if (client?.frameType === 'top-level') {
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {Client | undefined} client
|
||||
* @param {string} requestId
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async function getResponse(event, client, requestId) {
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const requestClone = event.request.clone()
|
||||
|
||||
function passthrough() {
|
||||
// Cast the request headers to a new Headers instance
|
||||
// so the headers can be manipulated with.
|
||||
const headers = new Headers(requestClone.headers)
|
||||
|
||||
// Remove the "accept" header value that marked this request as passthrough.
|
||||
// This prevents request alteration and also keeps it compliant with the
|
||||
// user-defined CORS policies.
|
||||
const acceptHeader = headers.get('accept')
|
||||
if (acceptHeader) {
|
||||
const values = acceptHeader.split(',').map((value) => value.trim())
|
||||
const filteredValues = values.filter(
|
||||
(value) => value !== 'msw/passthrough',
|
||||
)
|
||||
|
||||
if (filteredValues.length > 0) {
|
||||
headers.set('accept', filteredValues.join(', '))
|
||||
} else {
|
||||
headers.delete('accept')
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(requestClone, { headers })
|
||||
}
|
||||
|
||||
// Bypass mocking when the client is not active.
|
||||
if (!client) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
// The absence of the immediate/parent client in the map of the active clients
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Notify the client that a request has been intercepted.
|
||||
const serializedRequest = await serializeRequest(event.request)
|
||||
const clientMessage = await sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
...serializedRequest,
|
||||
},
|
||||
},
|
||||
[serializedRequest.body],
|
||||
)
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_RESPONSE': {
|
||||
return respondWithMock(clientMessage.data)
|
||||
}
|
||||
|
||||
case 'PASSTHROUGH': {
|
||||
return passthrough()
|
||||
}
|
||||
}
|
||||
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Client} client
|
||||
* @param {any} message
|
||||
* @param {Array<Transferable>} transferrables
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
function sendToClient(client, message, transferrables = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(message, [
|
||||
channel.port2,
|
||||
...transferrables.filter(Boolean),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @returns {Response}
|
||||
*/
|
||||
function respondWithMock(response) {
|
||||
// Setting response status code to 0 is a no-op.
|
||||
// However, when responding with a "Response.error()", the produced Response
|
||||
// instance will have status code set to 0. Since it's not possible to create
|
||||
// a Response instance with status code 0, handle that use-case separately.
|
||||
if (response.status === 0) {
|
||||
return Response.error()
|
||||
}
|
||||
|
||||
const mockedResponse = new Response(response.body, response)
|
||||
|
||||
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
|
||||
value: true,
|
||||
enumerable: true,
|
||||
})
|
||||
|
||||
return mockedResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
*/
|
||||
async function serializeRequest(request) {
|
||||
return {
|
||||
url: request.url,
|
||||
mode: request.mode,
|
||||
method: request.method,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
cache: request.cache,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: await request.arrayBuffer(),
|
||||
keepalive: request.keepalive,
|
||||
}
|
||||
}
|
7
.storybook/storybook-addon-vitest.d.ts
vendored
Normal file
7
.storybook/storybook-addon-vitest.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// The addon package.json incorrectly exports types, so we need to override them here.
|
||||
// See: https://github.com/storybookjs/storybook/blob/v9.0.4/code/addons/vitest/package.json#L70-L76
|
||||
declare module '@storybook/addon-vitest/vitest-plugin' {
|
||||
export * from '@storybook/addon-vitest/dist/vitest-plugin/index';
|
||||
}
|
||||
|
||||
export {};
|
7
.storybook/storybook-theme.ts
Normal file
7
.storybook/storybook-theme.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { create } from 'storybook/theming';
|
||||
|
||||
export default create({
|
||||
base: 'light',
|
||||
brandTitle: 'Mastodon Storybook',
|
||||
brandImage: 'https://joinmastodon.org/logos/wordmark-black-text.svg',
|
||||
});
|
8
.storybook/vitest.setup.ts
Normal file
8
.storybook/vitest.setup.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
|
||||
import { setProjectAnnotations } from '@storybook/react-vite';
|
||||
|
||||
import * as projectAnnotations from './preview';
|
||||
|
||||
// This is an important step to apply the right configuration when testing your stories.
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
330
CHANGELOG.md
330
CHANGELOG.md
|
@ -2,9 +2,333 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.4.1] - 2025-07-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix nearly every sub-directory being crawled as part of Vite build (#35323 by @ClearlyClaire)
|
||||
- Fix assets not building when Redis is unavailable (#35321 by @oneiros)
|
||||
- Fix replying from media modal or pop-in-player tagging user `@undefined` (#35317 by @ClearlyClaire)
|
||||
- Fix support for special characters in various environment variables (#35314 by @mjankowski and @ClearlyClaire)
|
||||
- Fix some database migrations failing for indexes manually removed by admins (#35309 by @mjankowski)
|
||||
|
||||
## [4.4.0] - 2025-07-08
|
||||
|
||||
### Added
|
||||
|
||||
- **Add “Followers you know” widget to user profiles and hover cards** (#34652, #34678, #34681, #34697, #34699, #34769, #34774 and #34914 by @diondiondion)
|
||||
- **Add featured tab to profiles on web UI and rework pinned posts** (#34405, #34483, #34491, #34754, #34855, #34858, #34868, #34869, #34927, #34995, #35056 and #34931 by @ChaosExAnima, @ClearlyClaire, @Gargron, and @diondiondion)
|
||||
- Add endorsed accounts to featured tab in web UI (#34421 and #34568 by @Gargron)\
|
||||
This also includes the following new REST API endpoints:
|
||||
- `GET /api/v1/accounts/:id/endorsements`: https://docs.joinmastodon.org/methods/accounts/#endorsements
|
||||
- `POST /api/v1/accounts/:id/endorse`: https://docs.joinmastodon.org/methods/accounts/#endorse
|
||||
- `POST /api/v1/accounts/:id/unendorse`: https://docs.joinmastodon.org/methods/accounts/#unendorse
|
||||
- Add ability to add and remove hashtags from featured tags in web UI (#34489, #34887, and #34490 by @ClearlyClaire and @Gargron)\
|
||||
This is achieved through the new REST API endpoints:
|
||||
- `POST /api/v1/tags/:id/feature`: https://docs.joinmastodon.org/methods/tags/#feature
|
||||
- `POST /api/v1/tags/:id/unfeature`: https://docs.joinmastodon.org/methods/tags/#unfeature
|
||||
- Add reminder when about to post without alt text in web UI (#33760 and #33784 by @Gargron)
|
||||
- Add a warning in Web UI when composing a post when the selected and detected language are different (#33042, #33683, #33700, #33724, #33770, and #34193 by @ClearlyClaire and @Gargron)
|
||||
- Add support for verifying and displaying remote quote posts (#34370, #34481, #34510, #34551, #34480, #34479, #34553, #34584, #34623, #34738, #34766, #34770, #34772, #34773, #34786, #34790, #34864, #34957, #34961, #35016, #35022, #35036, #34946, #34945 and #34958 by @ClearlyClaire and @diondiondion)\
|
||||
Support for verifying remote quotes according to [FEP-044f](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md) and displaying them in the Web UI has been implemented.\
|
||||
Quoting other people is not implemented yet, and it is currently not possible to mark your own posts as allowing quotes. However, a new “Who can quote” setting has been added to the “Posting defaults” section of the user settings. This setting allows you to set a default that will be used for new posts made on Mastodon 4.5 and newer, when quote posts will be fully implemented.\
|
||||
In the REST API, quote posts are represented by a new `quote` attribute on `Status` and `StatusEdit` entities: https://docs.joinmastodon.org/entities/StatusEdit/#quote https://docs.joinmastodon.org/entities/Status/#quote
|
||||
- Add ability to reorder and translate server rules (#34637, #34737, #34494, #34756, #34820, #34997, #35170, #35174 and #35174 by @ChaosExAnima and @ClearlyClaire)\
|
||||
Rules are now shown in the user’s language, if a translation has been set.\
|
||||
In the REST API, `Rule` entities now have a new `translations` attribute: https://docs.joinmastodon.org/entities/Rule/#translations
|
||||
- Add emoji from Twemoji 15.1.0, including in the emoji picker/completion (#33395, #34321, #34620, and #34677 by @ChaosExAnima, @ClearlyClaire, @TheEssem, and @eramdam)
|
||||
- Add option to remove account from followers in web UI (#34488 by @Gargron)
|
||||
- Add relationship tags to profiles and hover cards in web UI (#34467 and #34792 by @Gargron and @diondiondion)
|
||||
- Add ability to open posts in a new tab by middle-clicking in web UI (#32988, #33106, #33419, and #34700 by @ClearlyClaire, @Gargron, and @tribela)
|
||||
- Add new filter action to blur media (#34256 by @ClearlyClaire)\
|
||||
In the REST API, this adds a new possible value of `blur` to the `filter_action` attribute: https://docs.joinmastodon.org/entities/Filter/#filter_action
|
||||
- Add dropdown menu to hashtag links in web UI (#34393 by @Gargron)
|
||||
- **Add server setting to allow referrer** (#33214, #33239, #33903, and #34731 by @ChaosExAnima, @ClearlyClaire, @Gargron, and @renchap)\
|
||||
In order to protect the privacy of users of small or thematic servers, Mastodon previously avoided transmitting referrer information when clicking outside links, which unfortunately made Mastodon completely invisible to other websites, even though the privacy implications on large generic servers are very limited.\
|
||||
Server administrators can now chose to opt in to transmit referrer information when following an external link. Only the domain name is transmitted, not the referrer path.
|
||||
- Add double tap to zoom and swipe to dismiss to media modal in web UI (#34210 by @Gargron)
|
||||
- Add link from Web UI for Hashtags to the Moderation UI (#31448 by @ThisIsMissEm)
|
||||
- **Add terms of service** (#33055, #33233, #33230, #33703, #33699, #33994, #33993, #34105, #34122, #34200, #34527, #35053, #35115, #35126, #35127 and #35233 by @ClearlyClaire, @Gargron, @mjankowski, and @oneiros)\
|
||||
Server administrators can now fill in Terms of Service and notify their users of upcoming changes.
|
||||
- Add optional bulk mailer settings (#35191 and #35203 by @oneiros)\
|
||||
This adds the optional environment variables `BULK_SMTP_PORT`, `BULK_SMTP_SERVER`, `BULK_SMTP_LOGIN` and so on analogous to `SMTP_PORT`, `SMTP_SERVER`, `SMTP_LOGIN` and related SMTP configuration environment variables.\
|
||||
When `BULK_SMTP_SERVER` is set, this group of variables is used instead of the regular ones for sending announcement notification emails and Terms of Service notification emails.
|
||||
- **Add age verification on sign-up** (#34150, #34663, and #34636 by @ClearlyClaire and @Gargron)\
|
||||
Server administrators now have a setting to set a minimum age requirement for creating a new server, asking users for their date of birth. The date of birth is checked against the minimum age requirement server-side but not stored.\
|
||||
The following REST API changes have been made to accommodate this:
|
||||
- `registrations.min_age` has been added to the `Instance` entity: https://docs.joinmastodon.org/entities/Instance/#registrations-min_age
|
||||
- the `date_of_birth` parameter has been added to the account creation API: https://docs.joinmastodon.org/methods/accounts/#create
|
||||
- Add ability to dismiss alt text badge by tapping it in web UI (#33737 by @Gargron)
|
||||
- Add loading indicator to timeline gap indicators in web UI (#33762 by @Gargron)
|
||||
- Add interaction modal when trying to interact with a poll while logged out (#32609 by @ThisIsMissEm)
|
||||
- **Add experimental FASP support** (#34031, #34415, #34765, #34965, #34964, #34033, #35218, #35262 and #35263 by @oneiros)\
|
||||
This is a first step towards supporting “Fediverse Auxiliary Service Providers” (https://github.com/mastodon/fediverse_auxiliary_service_provider_specifications). This is mostly interesting to developers who would like to implement their own FASP, but also includes the capability to share data with a discovery provider (see https://www.fediscovery.org).
|
||||
- Add ability for admins to send announcements to all users via email (#33928 and #34411 by @ClearlyClaire)\
|
||||
This is meant for critical announcements only, as this will potentially send a lot of emails and cannot be opted out of by users.
|
||||
- Add Server Moderation Notes (#31529 by @ThisIsMissEm)
|
||||
- Add loading spinner to “Post” button when sending a post (#35153 by @diondiondion)
|
||||
- Add option to use system scrollbar styling (#32117 by @vmstan)
|
||||
- Add hover cards to follow suggestions (#33749 by @ClearlyClaire)
|
||||
- Add `t` hotkey for post translations (#33441 by @ClearlyClaire)
|
||||
- Add timestamp to all announcements in Web UI (#18329 by @ClearlyClaire)
|
||||
- Add dropdown menu with quick actions to lists of accounts in web UI (#34391, #34709, and #34767 by @Gargron, @diondiondion, and @mkljczk)
|
||||
- Add support for displaying “year in review” notification in web UI (#32710, #32765, #32709, #32807, #32914, #33148, and #33882 by @Gargron and @mjankowski)\
|
||||
Note that the notification is currently not generated automatically, and at the moment requires a manual undocumented administrator action.
|
||||
- Add experimental support for receiving HTTP Message Signatures (RFC9421) (#34814, #35033, #35109 and #35278 by @oneiros)\
|
||||
For now, this needs to be explicitly enabled through the `http_message_signatures` feature flag (`EXPERIMENTAL_FEATURES=http_message_signatures`). This currently only covers verifying such signatures (inbound HTTP requests), not issuing them (outbound HTTP requests).
|
||||
- Add experimental Async Refreshes API (#34918 by @oneiros)
|
||||
- Add experimental server-side feature to fetch remote replies (#32615, #34147, #34149, #34151, #34615, #34682, and #34702 by @ClearlyClaire and @sneakers-the-rat)\
|
||||
This experimental feature causes the server to recursively fetch replies in background tasks whenever a user opens a remote post. This happens asynchronously and the client is currently not notified of the existence of new replies, which will thus only be displayed the next time this post’s context gets requested.\
|
||||
This feature needs to be explicitly enabled server-side by setting `FETCH_REPLIES_ENABLED` environment variable to `true`.
|
||||
- Add simple feature flag system through the `EXPERIMENTAL_FEATURES` environment variable (#34038 and #34124 by @oneiros)\
|
||||
This allows enabling comma-separated feature flags for experimental features.\
|
||||
The current supported feature flags are `inbound_quotes`, `fasp` and `http_message_signatures`.
|
||||
- Add `dev:populate_sample_data` rake task to populate test data (#34676, #34733, #34771, #34787, and #34791 by @ClearlyClaire and @diondiondion)
|
||||
- Add support for displaying fallback representation when receiving MathML (#27107 by @4e554c4c)
|
||||
- Add warning for Elasticsearch index analyzers mismatch (#34515 and #34567 by @ClearlyClaire and @Gargron)
|
||||
- Add `-only-mapping` option to `tootctl search deploy` (#34466 and #34566 by @Gargron)
|
||||
- Add server-side support for grouping account sign-up notifications (#34298 by @ClearlyClaire)
|
||||
- Add `registrations.reason_required` attribute to `/api/v2/instance` response (#34280 by @ClearlyClaire)\
|
||||
This is documented at https://docs.joinmastodon.org/entities/Instance/#registrations-reason_required
|
||||
- Add `EXTRA_MEDIA_HOSTS` environment variable to add extra hosts to Content-Security-Policy (#34184 by @shleeable)
|
||||
- Add `Deprecation` headers on deprecated API endpoints (#34262 and #34397 by @ClearlyClaire)\
|
||||
This is documented at https://docs.joinmastodon.org/api/guidelines/#deprecations
|
||||
- Add `about`, `privacy_policy` and `terms_of_service` URLs to `/api/v2/instance` (#33849 by @ClearlyClaire)
|
||||
- Add API to delete media attachments that are not in use (#33991 and #34035 by @ClearlyClaire and @ThisIsMissEm)\
|
||||
`DELETE /api/v1/media/:id`: https://docs.joinmastodon.org/methods/media/#delete
|
||||
- Add optional `delete_media` parameter to `DELETE /api/v1/statuses/:id` (#33988 by @ClearlyClaire)\
|
||||
This is documented at https://docs.joinmastodon.org/methods/statuses/#delete
|
||||
- Add `og:locale` to expose status language in OpenGraph previews (#34012 by @ThisIsMissEm)
|
||||
- Add `-skip-filled-timeline` option to `tootctl feed build` to skip half-filled feeds (#33844 by @ClearlyClaire)
|
||||
- Add support for changing the base Docker registry with the `BASE_REGISTRY` `ARG` (#33712 by @wolfspyre)
|
||||
- Add an optional metric exporter (#33734, #33840, #34172, #34192, #34223, and #35005 by @oneiros and @renchap)\
|
||||
Optionally enable the `prometheus_exporter` ruby gem (see https://github.com/discourse/prometheus_exporter) to collect and expose metrics. See the documentation for all the details: https://docs.joinmastodon.org/admin/config/#prometheus
|
||||
- Add `attribution_domains` attribute to `PATCH /api/v1/accounts/update_credentials` (#32730 by @c960657)\
|
||||
This is documented at https://docs.joinmastodon.org/methods/accounts/#update_credentials
|
||||
- Add support for standard WebPush in addition to previous draft (#33572, #33528, and #33587 by @ClearlyClaire and @p1gp1g)
|
||||
- Add support for Active Record query log tags (#33342 by @renchap)
|
||||
- Add OTel trace & span IDs to logs (#33339 and #33362 by @renchap)
|
||||
- Add missing `on_delete: :cascade` foreign keys option to various database columns (#33175 by @mjankowski)
|
||||
- Add explicit migration breakpoints (#33089 by @ClearlyClaire)
|
||||
- Add rel alternate rss/json links to pages for tags (#33179 by @mjankowski)
|
||||
- Add media attachment description limit to instance API response (#33153 by @mjankowski)\
|
||||
This adds the `configuration.media_attachments.description_limit` attribute to the `Instance` entity, documented at https://docs.joinmastodon.org/entities/Instance/#description_limit
|
||||
- Add `maxlength` to registration reason input (#33162 by @mjankowski)
|
||||
- Add `REPLICA_PREPARED_STATEMENTS` and `REPLICA_DB_TASKS` environment variables (#32908 by @shleeable)\
|
||||
See documentation at https://docs.joinmastodon.org/admin/scaling/#read-replicas
|
||||
- Add a range of reserved usernames to reduce potential misuse by malicious actors (#32828 by @jmking-iftas)
|
||||
- Add operations on relays to the admin audit log (#32819 by @ThisIsMissEm)
|
||||
- Add userinfo OAuth endpoint (#32548 by @ThisIsMissEm)
|
||||
- Add the standard VCS attributes to OpenTelemetry spans (#32904 by @renchap)
|
||||
- Add endpoint to remove web push subscription (#32626 by @oneiros)\
|
||||
Mastodon now sets a new `Unsubscribe-URL` request header when performing WebPush requests. This URL can be used by the WebPush server to disable the WebPush subscription on Mastodon’s side in case of unfixable errors.
|
||||
- Add missing content warning text to RSS feeds (#32406 by @mjankowski)
|
||||
- Add Swiss German to languages dropdown (#29281 by @FlohEinstein)
|
||||
|
||||
### Changed
|
||||
|
||||
- Change design of navigation panel in Web UI, change layout on narrow screens (#34910, #34987, #35017, #34986, #35029, #35065, #35067, #35072, #35074, #35075, #35101, #35173, #35183, #35193 and #35225 by @ClearlyClaire, @Gargron, and @diondiondion)
|
||||
- Change design of lists in web UI (#32881, #33054, and #33036 by @Gargron)
|
||||
- Change design of edit media modal in web UI (#33516, #33702, #33725, #33725, #33771, and #34345 by @Gargron)
|
||||
- Change design of audio player in web UI (#34520, #34740, #34865, #34929, #34933, and #35034 by @ClearlyClaire, @Gargron, and @diondiondion)
|
||||
- Change design of interaction modal in web UI (#33278 by @Gargron)
|
||||
- Change list timelines to reflect added and removed users retroactively (#32930 by @Gargron)
|
||||
- Change account search to be more forgiving of spaces (#34455 by @Gargron)
|
||||
- Change unfollow button label from “Mutual” to “Unfollow” in web UI (#34392 by @Gargron)
|
||||
- Change “Specific people” to “Private mention” in menu in web UI (#33963 by @Gargron)
|
||||
- Change "Explore" to "Trending" and remove explanation banners (#34985 by @Gargron)
|
||||
- Change media attachments of moderated posts to not be accessible (#34872 by @Gargron)
|
||||
Moderators will still be able to access them while they are kept, but they won't be accessible to the public in the meantime.
|
||||
- Change language names in compose box language picker to be localized (#33402 by @c960657)
|
||||
- Change onboarding flow in web UI (#32998, #33119, #33471 and #34962 by @ClearlyClaire and @Gargron)
|
||||
- Change Advanced Web UI to use the new main menu instead of the “Getting started” column (#35117 by @diondiondion)
|
||||
- Change emoji categories in admin interface to be ordered by name (#33630 by @ShadowJonathan)
|
||||
- Change design of rich text elements in web UI (#32633 by @Gargron)
|
||||
- Change wording of “single choice” to “pick one” in poll authoring form (#32397 by @ThisIsMissEm)
|
||||
- Change returned favorite and boost counts to use those provided by the remote server, if available (#32620, #34594, #34618, and #34619 by @ClearlyClaire and @sneakers-the-rat)
|
||||
- Change label of favourite notifications on private mentions (#31659 by @ClearlyClaire)
|
||||
- Change wording of "discard draft?" confirmation dialogs (#35192 by @diondiondion)
|
||||
- Change `libvips` to be enabled by default in place of ImageMagick (#34741 and #34753 by @ClearlyClaire and @diondiondion)
|
||||
- Change avatar and header size limits from 2MB to 8MB when using libvips (#33002 by @Gargron)
|
||||
- Change search to use query params in web UI (#32949 and #33670 by @ClearlyClaire and @Gargron)
|
||||
- Change build system from Webpack to Vite (#34454, #34450, #34758, #34768, #34813, #34808, #34837, #34732, #35007, #35035 and #35177 by @ChaosExAnima, @ClearlyClaire, @mjankowski, and @renchap)
|
||||
- Change account creation API to forbid creation from user tokens (#34828 by @ThisIsMissEm)
|
||||
- Change `/api/v2/instance` to be enabled without authentication when limited federation mode is enabled (#34576 by @ClearlyClaire)
|
||||
- Change `DEFAULT_LOCALE` to not override unauthenticated users’ browser language (#34535 by @ClearlyClaire)\
|
||||
If you want to preserve the old behavior, you can add `FORCE_DEFAULT_LOCALE=true`.
|
||||
- Change size of profile picture on profile page from 90px to 92px (#34807 by @larouxn)
|
||||
- Change passthrough video processing to emit `moov` atom at start of video (#34726 by @ClearlyClaire)
|
||||
- Change kerning to be disabled for Japanese text to preserve monospaced alignment for readability (#34448 by @nagutabby)
|
||||
- Change error handling of various endpoints to return 422 instead of 500 on invalid parameters (#29308, #34434, and #34452 by @danielmbrasil and @mjankowski)
|
||||
- Change Web UI to use `<time>` tags for various timestamps (#34131 by @scarf005)
|
||||
- Change devcontainer to be accessible from local network (#34269 by @ChaosExAnima)
|
||||
- Change video transcoding code to skip re-encoding yuvj420p videos (#34098 by @rinsuki)
|
||||
- Change web client settings to be saved earlier and more often (#34074 by @ClearlyClaire)
|
||||
- Change test coverage report generation to be disabled by default, with opt-in through the `COVERAGE` environment variable (#33824 by @mjankowski)
|
||||
- Change devcontainer to store bootsnap cache outside of bind mounts (#33677 by @c960657)
|
||||
- Change error handling in the `mastodon:setup` rake task to summarize encountered errors at the end (#33603 by @mjankowski)
|
||||
- Change tooltip of some moderation interface timestamps to include time in addition to date (#33191 by @ThisIsMissEm)
|
||||
- Change organization and wording of `README.md`, `CONTRIBUTING.md` and `DEVELOPMENT.md` (#32143, #33328, #33517, #33637, #33728, #34675, and #34761 by @Lamparter, @andypiper, @diondiondion, @larouxn, @mikkelricky, and @mjankowski)
|
||||
- Change custom CSS to be cached for longer and invalidated based on its contents (#33207 and #33583 by @mjankowski and @tribela)
|
||||
- Change `tootctl maintenance fix-duplicates` to disable database statement timeouts (#33484 by @mjankowski)
|
||||
- Change some icons in settings sidebar to avoid “double icon” near each other (#33449 by @mjankowski)
|
||||
- Change animation on feed generation screen in web UI (#33311 by @Gargron)
|
||||
- Change OTel instrumentation to not start traces with Redis spans (#33090 by @robbkidd)
|
||||
- Change new post delivery to skip suspended followers (#27509 and #33030 by @ClearlyClaire and @oneiros)
|
||||
- Change URL truncation to account for ellipses (#33229 by @FND)
|
||||
- Change ability to navigate of unconfirmed users (#33209 by @Gargron)
|
||||
- Change hashtag trends to be stored in the database instead of redis (#32837, #33189, and #34016 by @Gargron and @onekopaka)
|
||||
- Change “social web” to “fediverse” in a few banners in web UI (#33101 by @Gargron)
|
||||
- Change server rules to be collapsible (#33039 by @Gargron)
|
||||
- Change design of modal loading and error screens in web UI (#33092 by @Gargron)
|
||||
- Change error messages to be more accurate when failing to add an account to a list (#33082 by @Gargron)
|
||||
- Change timezone picker in the default settings to show the default timezone (#31803 by @c960657)
|
||||
- Change `tootctl accounts modify --disable-2fa` to remove webauthn credentials (#29883 by @mszpro)
|
||||
- Change preview card processing to be more liberal in what it accepts (#31357 by @c960657)
|
||||
- Change scheduled statuses to be discarded if the author’s account is frozen (#30729 by @PauloVilarinho)
|
||||
- Change display of statuses in admin panel (#30813 by @ThisIsMissEm)
|
||||
- Change parsing of `ALLOWED_PRIVATE_ADDRESSES` to happen at startup (#32850 by @ClearlyClaire)
|
||||
- Change WebPush delivery to skip notifications older than 2 days old (#32842 by @ThisIsMissEm)
|
||||
- Change PWA manifest to prefer official mobile apps (#27254 by @jake-anto)
|
||||
|
||||
### Removed
|
||||
|
||||
- **Remove support for Redis namespaces** (#34664 and #34665 by @ClearlyClaire)\
|
||||
See https://github.com/mastodon/redis_namespace_migration
|
||||
- Remove support for imports started on pre-4.2.0 Mastodon versions (#34371 by @mjankowski)
|
||||
- Remove support for PostgreSQL 12 and earlier (#34744 by @ClearlyClaire)
|
||||
- Remove support for Node.JS < 20 (#34390 by @renchap)
|
||||
- Remove support for Redis < 6.2 (#30413 by @ClearlyClaire)
|
||||
- Remove support for Ruby 3.1 (#32363 by @mjankowski)
|
||||
- Remove support for OAuth Password Grant Type (#30960 by @ThisIsMissEm)\
|
||||
https://docs.joinmastodon.org/spec/oauth/#token
|
||||
- Remove `OTP_SECRET` environment variable and legacy OTP code (#34743, #34757, #34748, and #34810 by @ClearlyClaire and @mjankowski)\
|
||||
This breaks zero-downtime migrations from versions earlier than 4.3.0.
|
||||
- Remove broken support for HTTP Basic Authentication (#34501 by @ThisIsMissEm)
|
||||
- Remove system tooltip for alt text in web UI (#33736 by @Gargron)
|
||||
- Remove `thing_type` and `thing_id` columns from settings table (#31971 and #33196 by @ClearlyClaire and @mjankowski)
|
||||
- Remove redundant temporary index creation in `tootctl status remove` (#33023 by @ClearlyClaire)
|
||||
- Remove duplicate indexes from database (#32454 by @mjankowski)
|
||||
- Remove redundant title attribute in column links (#32258 by @c960657)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix remote suspension of a user causing local instance to remove remote follows (#27588 by @ShadowJonathan)
|
||||
- Fix blocked accounts not being automatically removed from trending statuses (#34891 by @ClearlyClaire)
|
||||
- Fix nested buttons in search popout in web UI (#34871 by @Gargron)
|
||||
- Fix not being able to scroll dropdown on touch devices in web UI (#34873 by @Gargron)
|
||||
- Fix inconsistent filtering of silenced accounts for other silenced accounts (#34863 by @ClearlyClaire)
|
||||
- Fix update checker listing updates older or equal to current running version (#33906 by @ClearlyClaire)
|
||||
- Fix clicking a status multiple times causing duplicate entries in browser history (#35118 by @ClearlyClaire)
|
||||
- Fix “Alt text” button submitting form in moderation interface (#35147 by @ClearlyClaire)
|
||||
- Fix Firefox sometimes not updating spellcheck language in textarea (#35148 by @ClearlyClaire)
|
||||
- Fix `NoMethodError` in edge case of emoji cache handling (#34749 by @dariusk)
|
||||
- Fix handling of inlined `featured` collections in ActivityPub actor objects (#34789 and #34811 by @ClearlyClaire)
|
||||
- Fix long link names in admin sidebar being truncated (#34727 by @diondiondion)
|
||||
- Fix admin dashboard crash on specific Elasticsearch connection errors (#34683 by @ClearlyClaire)
|
||||
- Fix OIDC account creation failing for long display names (#34639 by @defnull)
|
||||
- Fix use of the deprecated `/api/v1/instance` endpoint in the moderation interface (#34613 by @renchap)
|
||||
- Fix inaccessible “Clear search” button (#35152 and #35281 by @diondiondion)
|
||||
- Fix search operators sometimes getting lost (#35190 by @ClearlyClaire)
|
||||
- Fix directory scroll position reset (#34560 by @przucidlo)
|
||||
- Fix needlessly complex SVG paths for oEmbed and logo (#34538 by @edent)
|
||||
- Fix avatar sizing with long account name in some UI elements (#34514 by @gomasy)
|
||||
- Fix empty menu section in status dropdown (#34431 by @ClearlyClaire)
|
||||
- Fix the delete suggestion button not working (#34396 and #34398 by @ClearlyClaire and @renchap)
|
||||
- Fix popover/dialog backgrounds not being blurred on older Webkit browsers (#35220 by @diondiondion)
|
||||
- Fix radio buttons not always being correctly centered (#34389 by @ChaosExAnima)
|
||||
- Fix visual glitches with adding post filters (#34387 by @ChaosExAnima)
|
||||
- Fix bugs with upload progress (#34325 by @ChaosExAnima)
|
||||
- Fix being unable to hide controls in full screen video in web UI (#34308 by @Gargron)
|
||||
- Fix extra space under left-indented vertical videos (#34313 by @ClearlyClaire)
|
||||
- Fix glitchy iOS media attachment drag interactions (#35057 by @diondiondion)
|
||||
- Fix zoomed images being blurry in Safari (#35052 by @diondiondion)
|
||||
- Fix redundant focus stop within status component in Web UI and make focus style more noticeable (#35037, #35051, #35096, #35150 and #35251 by @diondiondion)
|
||||
- Fix digits in media player time readout not having a consistent width (#35038 by @diondiondion)
|
||||
- Fix wrong text color for “Open in advanced web interface” banner in high-contrast theme (#35032 by @diondiondion)
|
||||
- Fix hover card for limited accounts not hiding information as expected (#35024 by @diondiondion)
|
||||
- Fix some animations not respecting the reduced animation preferences (#35018 by @ChaosExAnima)
|
||||
- Fix direction of media gallery arrows in RTL locales (#35014 by @diondiondion)
|
||||
- Fix cramped layout of follower recommendations on small viewports (#34967 and #35023 by @diondiondion)
|
||||
- Fix two composers being shown at the same time in some cases (#35006 by @ChaosExAnima)
|
||||
- Fix handling of remote attachments with multiple media types (#34996 by @ClearlyClaire)
|
||||
- Fix broken colors in some themed SVGs in web UI (#34988 by @Gargron)
|
||||
- Fix wrong dimensions on blurhash previews of news articles in web UI (#34990 by @Gargron)
|
||||
- Fix wrong styles on action bar in media modal in web UI (#34989 by @Gargron)
|
||||
- Fix search column input not updating on param change (#34951 by @PGrayCS)
|
||||
- Fix account note textarea being interactable before the relationship gets fetched (#34932 by @ClearlyClaire)
|
||||
- Fix SASS deprecation notices (#34278 by @ChaosExAnima)
|
||||
- Fix display of failed-to-load image attachments in web UI (#34217 by @Gargron)
|
||||
- Fix duplicate REST API requests on submitting account personal note with ctrl+enter (#34213 by @ClearlyClaire)
|
||||
- Fix unnecessary rerenders in composer dropdown menu (#34133 by @ClearlyClaire)
|
||||
- Fix behavior of database schema loading with `SKIP_POST_DEPLOYMENT_MIGRATIONS` (#34089 by @ClearlyClaire)
|
||||
- Fix infinite scroll not working on profile media tab in web UI (#33860 and #34171 by @ClearlyClaire and @Gargron)
|
||||
- Fix minor inefficiencies in domain suspension code (#33897 by @larouxn)
|
||||
- Fix potential inefficiency in media privacy system check (#33858 by @ClearlyClaire)
|
||||
- Fix public timeline inefficiency by adding the `language` column to the public timelines index (#33779 by @ClearlyClaire)
|
||||
- Fix re-encoding of high-framerate VFR videos with FFmpeg 6+ (#33634 by @ClearlyClaire)
|
||||
- Fix error when processing invalid `Announce` activity with missing object (#33570 by @ShadowJonathan)
|
||||
- Fix color contrast in report modal (#33468 by @ClearlyClaire)
|
||||
- Fix error 500 when passing an invalid `lang` parameter (#33467 by @ClearlyClaire)
|
||||
- Fix `/share` not using server-set characters limit (#33459 by @kescherCode)
|
||||
- Fix audio player modal having white-on-white buttons in light theme (#33444 by @ClearlyClaire)
|
||||
- Fix favorite & bookmark text toggle in timeline, status and image view (#27209 by @gunchleoc)
|
||||
- Fix Web UI erroneously stopping to offer expanding search results after second page (#33428 by @ClearlyClaire)
|
||||
- Fix missing value limits for `UserRole` position (#33172 and #33349 by @mjankowski)
|
||||
- Fix clicking on a profile mention while logged out potentially leading to incorrect account (#33324 by @ClearlyClaire)
|
||||
- Fix missing `NOT NULL` constraints on various database columns (#33244, #33284, #33308, #33330, #33374, and #34498 by @ClearlyClaire and @mjankowski)
|
||||
- Fix long account username overflowing on profiles (#33286 by @mjankowski)
|
||||
- Fix Vagrant failure to sync dangling symlinks (#28101 by @filippog)
|
||||
- Fix Chromium showing scrollbar on embedded posts (#33237 by @ClearlyClaire)
|
||||
- Fix missing top border on Admin Hashtags UI (#31443 by @ThisIsMissEm)
|
||||
- Fix design of search bar on explore screen in light theme in web UI (#33224 by @Gargron)
|
||||
- Fix various visual sign-up flow issues (#33206 by @Gargron)
|
||||
- Fix support of bidi text in account profiles (#33088 by @mokazemi)
|
||||
- Fix wording of the error returned when scheduling a status too soon (#33156 by @mjankowski)
|
||||
- Fix `inbox_url` presence on Relay not being validated (#32364 by @mjankowski)
|
||||
- Fix ability to include multiple copies of `embed.js` (#33107 by @YKWeyer)
|
||||
- Fix `rel="me"` check being case-sensitive (#32238 by @c960657)
|
||||
- Fix wrong video dimensions for some rotated videos (#33008 and #33261 by @Gargron and @tribela)
|
||||
- Fix error when viewing statuses to deleted replies in moderation view (#32986 by @ClearlyClaire)
|
||||
- Fix missing autofocus on boost modal (#32953 by @tribela)
|
||||
- Fix logic in “last used at per application” OAuth token list (#32912 by @mjankowski)
|
||||
- Fix admin dashboard linking to pages the user does not have permission to see (#32843 by @ThisIsMissEm)
|
||||
- Fix backspace navigation hotkey going back two pages instead of one on some browsers (#32826 by @c960657)
|
||||
- Fix typo in translation string (#32821 by @ThisIsMissEm)
|
||||
- Fix list of follow requests not having a back button (#32797 by @ClearlyClaire)
|
||||
- Fix out-of-view post contents being inconsistent with in-view post contents (#32778, #32887, and #32895 by @ClearlyClaire)
|
||||
- Fix `httplog` gem being used in production (#32776 and #32796 by @ClearlyClaire and @oneiros)
|
||||
- Fix use of deprecated `execCommand` for copying text by using the `clipboard` API (#32598 by @renchap)
|
||||
- Fix some translation strings not being properly pluralized (#27094 by @gunchleoc)
|
||||
|
||||
## [4.3.8] - 2025-05-06
|
||||
|
||||
### Security
|
||||
|
||||
- Update dependencies
|
||||
- Check scheme on account, profile, and media URLs ([GHSA-x2rc-v5wx-g3m5](https://github.com/mastodon/mastodon/security/advisories/GHSA-x2rc-v5wx-g3m5))
|
||||
|
||||
### Added
|
||||
|
||||
- Add warning for REDIS_NAMESPACE deprecation at startup (#34581 by @ClearlyClaire)
|
||||
- Add built-in context for interaction policies (#34574 by @ClearlyClaire)
|
||||
|
||||
### Changed
|
||||
|
||||
- Change activity distribution error handling to skip retrying for deleted accounts (#33617 by @ClearlyClaire)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove double-query for signed query strings (#34610 by @ClearlyClaire)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549 by @ClearlyClaire)
|
||||
- Fix sign-up e-mail confirmation page reloading on error or redirect (#34548 by @ClearlyClaire)
|
||||
|
||||
## [4.3.7] - 2025-04-02
|
||||
|
||||
### Add
|
||||
### Added
|
||||
|
||||
- Add delay to profile updates to debounce them (#34137 by @ClearlyClaire)
|
||||
- Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire)
|
||||
|
@ -210,7 +534,7 @@ The following changelog entries focus on changes visible to users, administrator
|
|||
- `GET /api/v2/notifications`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-grouped
|
||||
- `GET /api/v2/notifications/:group_key`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-notification-group
|
||||
- `GET /api/v2/notifications/:group_key/accounts`: https://docs.joinmastodon.org/methods/grouped_notifications/#get-group-accounts
|
||||
- `POST /api/v2/notifications/:group_key/dimsiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
|
||||
- `POST /api/v2/notifications/:group_key/dismiss`: https://docs.joinmastodon.org/methods/grouped_notifications/#dismiss-group
|
||||
- `GET /api/v2/notifications/:unread_count`: https://docs.joinmastodon.org/methods/grouped_notifications/#unread-group-count
|
||||
- **Add notification policies, filtered notifications and notification requests** (#29366, #29529, #29433, #29565, #29567, #29572, #29575, #29588, #29646, #29652, #29658, #29666, #29693, #29699, #29737, #29706, #29570, #29752, #29810, #29826, #30114, #30251, #30559, #29868, #31008, #31011, #30996, #31149, #31220, #31222, #31225, #31242, #31262, #31250, #31273, #31310, #31316, #31322, #31329, #31324, #31331, #31343, #31342, #31309, #31358, #31378, #31406, #31256, #31456, #31419, #31457, #31508, #31540, #31541, #31723, #32062 and #32281 by @ClearlyClaire, @Gargron, @TheEssem, @mgmn, @oneiros, and @renchap)\
|
||||
The old “Block notifications from non-followers”, “Block notifications from people you don't follow” and “Block direct messages from people you don't follow” notification settings have been replaced by a new set of settings found directly in the notification column.\
|
||||
|
@ -541,7 +865,7 @@ The following changelog entries focus on changes visible to users, administrator
|
|||
- Fix empty environment variables not using default nil value (#27400 by @renchap)
|
||||
- Fix language sorting in settings (#27158 by @gunchleoc)
|
||||
|
||||
## |4.2.11] - 2024-08-16
|
||||
## [4.2.11] - 2024-08-16
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
@ -9,30 +9,56 @@ You can contribute in the following ways:
|
|||
- Contributing code to Mastodon by fixing bugs or implementing features
|
||||
- Improving the documentation
|
||||
|
||||
If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
||||
|
||||
Please review the org-level [contribution guidelines] for high-level acceptance
|
||||
criteria guidance.
|
||||
|
||||
[contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md
|
||||
criteria guidance and the [DEVELOPMENT] guide for environment-specific details.
|
||||
|
||||
## API Changes and Additions
|
||||
|
||||
Please note that any changes or additions made to the API should have an accompanying pull request on [our documentation repository](https://github.com/mastodon/documentation).
|
||||
Any changes or additions made to the API should have an accompanying pull
|
||||
request on our [documentation repository].
|
||||
|
||||
## Bug reports
|
||||
## Bug Reports
|
||||
|
||||
Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected.
|
||||
Bug reports and feature suggestions must use descriptive and concise titles and
|
||||
be submitted to [GitHub Issues]. Please use the search function to make sure
|
||||
there are not duplicate bug reports or feature requests.
|
||||
|
||||
## Security Issues
|
||||
|
||||
If you believe you have identified a security issue in Mastodon or our own apps,
|
||||
check [SECURITY].
|
||||
|
||||
## Translations
|
||||
|
||||
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
|
||||
Translations are community contributed via [Crowdin]. They are periodically
|
||||
reviewed and merged into the codebase.
|
||||
|
||||
[](https://crowdin.com/project/mastodon)
|
||||
|
||||
## Pull requests
|
||||
## Pull Requests
|
||||
|
||||
**Please use clean, concise titles for your pull requests.** Unless the pull request is about refactoring code, updating dependencies or other internal tasks, assume that the person reading the pull request title is not a programmer or Mastodon developer, but instead a Mastodon user or server administrator, and **try to describe your change or fix from their perspective**. We use commit squashing, so the final commit in the main branch will carry the title of the pull request, and commits from the main branch are fed into the changelog. The changelog is separated into [keepachangelog.com categories](https://keepachangelog.com/en/1.0.0/), and while that spec does not prescribe how the entries ought to be named, for easier sorting, start your pull request titles using one of the verbs "Add", "Change", "Deprecate", "Remove", or "Fix" (present tense).
|
||||
### Size and Scope
|
||||
|
||||
Our time is limited and PRs making large, unsolicited changes are unlikely to
|
||||
get a response. Changes which link to an existing confirmed issue, or which come
|
||||
from a "help wanted" issue or other request are more likely to be reviewed.
|
||||
|
||||
The smaller and more narrowly focused the changes in a PR are, the easier they
|
||||
are to review and potentially merge. If the change only makes sense in some
|
||||
larger context of future ongoing work, note that in the description, but still
|
||||
aim to keep each distinct PR to a "smallest viable change" chunk of work.
|
||||
|
||||
### Description of Changes
|
||||
|
||||
Unless the Pull Request is about refactoring code, updating dependencies or
|
||||
other internal tasks, assume that the audience are not developers, but a
|
||||
Mastodon user or server admin, and try to describe it from their perspective.
|
||||
|
||||
The final commit in the main branch will carry the title from the PR. The main
|
||||
branch is then fed into the changelog and ultimately into release notes. We try
|
||||
to follow the [keepachangelog] spec, and while that does not prescribe how
|
||||
exactly the entries ought to be named, starting titles using one of the verbs
|
||||
"Add", "Change", "Deprecate", "Remove", or "Fix" (present tense) is helpful.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -40,16 +66,26 @@ Example:
|
|||
| ------------------------------------ | ------------------------------------------------------------- |
|
||||
| Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition |
|
||||
|
||||
It is not always possible to phrase every change in such a manner, but it is desired.
|
||||
### Technical Requirements
|
||||
|
||||
**The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable.
|
||||
Pull requests that do not pass automated checks on CI may not be reviewed. In
|
||||
particular, please keep in mind:
|
||||
|
||||
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
|
||||
|
||||
- Unit and integration tests (rspec, jest)
|
||||
- Unit and integration tests (rspec, vitest)
|
||||
- Code style rules (rubocop, eslint)
|
||||
- Normalization of locale files (i18n-tasks)
|
||||
- Relevant accessibility or performance concerns
|
||||
|
||||
## Documentation
|
||||
|
||||
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation).
|
||||
The [Mastodon documentation] is a statically generated site that contains guides
|
||||
and API docs. Improvements are made via PRs to the [documentation repository].
|
||||
|
||||
[contribution guidelines]: https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md
|
||||
[Crowdin]: https://crowdin.com/project/mastodon
|
||||
[DEVELOPMENT]: docs/DEVELOPMENT.md
|
||||
[documentation repository]: https://github.com/mastodon/documentation
|
||||
[GitHub Issues]: https://github.com/mastodon/mastodon/issues
|
||||
[keepachangelog]: https://keepachangelog.com/en/1.0.0/
|
||||
[Mastodon documentation]: https://docs.joinmastodon.org
|
||||
[SECURITY]: SECURITY.md
|
||||
|
|
388
Dockerfile
388
Dockerfile
|
@ -1,7 +1,7 @@
|
|||
# syntax=docker/dockerfile:1.9
|
||||
# syntax=docker/dockerfile:1.12
|
||||
|
||||
# This file is designed for production server deployment, not local development work
|
||||
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
|
||||
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/docs/DEVELOPMENT.md#docker
|
||||
|
||||
# Please see https://docs.docker.com/engine/reference/builder for information about
|
||||
# the extended buildx capabilities used in this file.
|
||||
|
@ -9,19 +9,20 @@
|
|||
# See: https://docs.docker.com/build/building/multi-platform/
|
||||
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
||||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||
ARG BASE_REGISTRY="docker.io"
|
||||
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
||||
# renovate: datasource=docker depName=docker.io/ruby
|
||||
ARG RUBY_VERSION="3.3.5"
|
||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||
ARG RUBY_VERSION="3.4.4"
|
||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||
# renovate: datasource=node-version depName=node
|
||||
ARG NODE_MAJOR_VERSION="20"
|
||||
ARG NODE_MAJOR_VERSION="22"
|
||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||
ARG DEBIAN_VERSION="bookworm"
|
||||
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm)
|
||||
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
||||
# Node.js image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||
FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
|
||||
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
|
||||
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
||||
|
||||
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
||||
# Example: v4.3.0-nightly.2023.11.09+pr-123456
|
||||
|
@ -29,6 +30,8 @@ FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
|||
ARG MASTODON_VERSION_PRERELEASE=""
|
||||
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
|
||||
ARG MASTODON_VERSION_METADATA=""
|
||||
# Will be available as Mastodon::Version.source_commit
|
||||
ARG SOURCE_COMMIT=""
|
||||
|
||||
# Allow Ruby on Rails to serve static files
|
||||
# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files
|
||||
|
@ -45,30 +48,31 @@ ARG GID="991"
|
|||
|
||||
# Apply Mastodon build options based on options above
|
||||
ENV \
|
||||
# Apply Mastodon version information
|
||||
# Apply Mastodon version information
|
||||
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
||||
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
||||
# Apply Mastodon static files and YJIT options
|
||||
SOURCE_COMMIT="${SOURCE_COMMIT}" \
|
||||
# Apply Mastodon static files and YJIT options
|
||||
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
||||
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
||||
# Apply timezone
|
||||
# Apply timezone
|
||||
TZ=${TZ}
|
||||
|
||||
ENV \
|
||||
# Configure the IP to bind Mastodon to when serving traffic
|
||||
# Configure the IP to bind Mastodon to when serving traffic
|
||||
BIND="0.0.0.0" \
|
||||
# Use production settings for Yarn, Node and related nodejs based tools
|
||||
# Use production settings for Yarn, Node.js and related tools
|
||||
NODE_ENV="production" \
|
||||
# Use production settings for Ruby on Rails
|
||||
# Use production settings for Ruby on Rails
|
||||
RAILS_ENV="production" \
|
||||
# Add Ruby and Mastodon installation to the PATH
|
||||
# Add Ruby and Mastodon installation to the PATH
|
||||
DEBIAN_FRONTEND="noninteractive" \
|
||||
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
|
||||
# Optimize jemalloc 5.x performance
|
||||
# Optimize jemalloc 5.x performance
|
||||
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
|
||||
# Enable libvips, should not be changed
|
||||
# Enable libvips, should not be changed
|
||||
MASTODON_USE_LIBVIPS=true \
|
||||
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
|
||||
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
|
||||
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
|
||||
|
||||
# Set default shell used for running commands
|
||||
|
@ -79,14 +83,14 @@ ARG TARGETPLATFORM
|
|||
RUN echo "Target platform is $TARGETPLATFORM"
|
||||
|
||||
RUN \
|
||||
# Remove automatic apt cache Docker cleanup scripts
|
||||
# Remove automatic apt cache Docker cleanup scripts
|
||||
rm -f /etc/apt/apt.conf.d/docker-clean; \
|
||||
# Sets timezone
|
||||
# Sets timezone
|
||||
echo "${TZ}" > /etc/localtime; \
|
||||
# Creates mastodon user/group and sets home directory
|
||||
# Creates mastodon user/group and sets home directory
|
||||
groupadd -g "${GID}" mastodon; \
|
||||
useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \
|
||||
# Creates /mastodon symlink to /opt/mastodon
|
||||
# Creates /mastodon symlink to /opt/mastodon
|
||||
ln -s /opt/mastodon /mastodon;
|
||||
|
||||
# Set /opt/mastodon as working directory
|
||||
|
@ -97,105 +101,92 @@ RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/
|
|||
|
||||
# hadolint ignore=DL3008,DL3005
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Apt update & upgrade to check for security updates to Debian image
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Apt update & upgrade to check for security updates to Debian image
|
||||
apt-get update; \
|
||||
apt-get dist-upgrade -yq; \
|
||||
# Install jemalloc, curl and other necessary components
|
||||
# Install jemalloc, curl and other necessary components
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
file \
|
||||
libjemalloc2 \
|
||||
patchelf \
|
||||
procps \
|
||||
tini \
|
||||
tzdata \
|
||||
wget \
|
||||
curl \
|
||||
file \
|
||||
libjemalloc2 \
|
||||
patchelf \
|
||||
procps \
|
||||
tini \
|
||||
tzdata \
|
||||
wget \
|
||||
; \
|
||||
# Patch Ruby to use jemalloc
|
||||
# Patch Ruby to use jemalloc
|
||||
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
|
||||
# Discard patchelf after use
|
||||
# Discard patchelf after use
|
||||
apt-get purge -y \
|
||||
patchelf \
|
||||
patchelf \
|
||||
;
|
||||
|
||||
# Create temporary build layer from base image
|
||||
FROM ruby AS build
|
||||
|
||||
# Copy Node package configuration files into working directory
|
||||
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
|
||||
COPY .yarn /opt/mastodon/.yarn
|
||||
|
||||
COPY --from=node /usr/local/bin /usr/local/bin
|
||||
COPY --from=node /usr/local/lib /usr/local/lib
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Install build tools and bundler dependencies from APT
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Install build tools and bundler dependencies from APT
|
||||
apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
libgdbm-dev \
|
||||
libglib2.0-dev \
|
||||
libgmp-dev \
|
||||
libicu-dev \
|
||||
libidn-dev \
|
||||
libpq-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libyaml-dev \
|
||||
meson \
|
||||
nasm \
|
||||
pkg-config \
|
||||
shared-mime-info \
|
||||
xz-utils \
|
||||
# libvips components
|
||||
libcgif-dev \
|
||||
libexif-dev \
|
||||
libexpat1-dev \
|
||||
libgirepository1.0-dev \
|
||||
libheif-dev/bookworm-backports \
|
||||
libimagequant-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
liblcms2-dev \
|
||||
liborc-dev \
|
||||
libspng-dev \
|
||||
libtiff-dev \
|
||||
libwebp-dev \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
libgdbm-dev \
|
||||
libglib2.0-dev \
|
||||
libgmp-dev \
|
||||
libicu-dev \
|
||||
libidn-dev \
|
||||
libpq-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libyaml-dev \
|
||||
meson \
|
||||
nasm \
|
||||
pkg-config \
|
||||
shared-mime-info \
|
||||
xz-utils \
|
||||
# libvips components
|
||||
libcgif-dev \
|
||||
libexif-dev \
|
||||
libexpat1-dev \
|
||||
libgirepository1.0-dev \
|
||||
libheif-dev/bookworm-backports \
|
||||
libimagequant-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
liblcms2-dev \
|
||||
liborc-dev \
|
||||
libspng-dev \
|
||||
libtiff-dev \
|
||||
libwebp-dev \
|
||||
# ffmpeg components
|
||||
libdav1d-dev \
|
||||
liblzma-dev \
|
||||
libmp3lame-dev \
|
||||
libopus-dev \
|
||||
libsnappy-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
libdav1d-dev \
|
||||
liblzma-dev \
|
||||
libmp3lame-dev \
|
||||
libopus-dev \
|
||||
libsnappy-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
;
|
||||
|
||||
RUN \
|
||||
# Configure Corepack
|
||||
rm /usr/local/bin/yarn*; \
|
||||
corepack enable; \
|
||||
corepack prepare --activate;
|
||||
|
||||
# Create temporary libvips specific build layer from build layer
|
||||
FROM build AS libvips
|
||||
|
||||
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
||||
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
||||
ARG VIPS_VERSION=8.15.3
|
||||
ARG VIPS_VERSION=8.17.1
|
||||
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
||||
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
||||
|
||||
|
@ -218,7 +209,7 @@ FROM build AS ffmpeg
|
|||
|
||||
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
|
||||
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
|
||||
ARG FFMPEG_VERSION=7.0.2
|
||||
ARG FFMPEG_VERSION=7.1
|
||||
# ffmpeg download URL, change with [--build-arg FFMPEG_URL="https://ffmpeg.org/releases"]
|
||||
ARG FFMPEG_URL=https://ffmpeg.org/releases
|
||||
|
||||
|
@ -232,28 +223,28 @@ WORKDIR /usr/local/ffmpeg/src/ffmpeg-${FFMPEG_VERSION}
|
|||
# Configure and compile ffmpeg
|
||||
RUN \
|
||||
./configure \
|
||||
--prefix=/usr/local/ffmpeg \
|
||||
--toolchain=hardened \
|
||||
--disable-debug \
|
||||
--disable-devices \
|
||||
--disable-doc \
|
||||
--disable-ffplay \
|
||||
--disable-network \
|
||||
--disable-static \
|
||||
--enable-ffmpeg \
|
||||
--enable-ffprobe \
|
||||
--enable-gpl \
|
||||
--enable-libdav1d \
|
||||
--enable-libmp3lame \
|
||||
--enable-libopus \
|
||||
--enable-libsnappy \
|
||||
--enable-libvorbis \
|
||||
--enable-libvpx \
|
||||
--enable-libwebp \
|
||||
--enable-libx264 \
|
||||
--enable-libx265 \
|
||||
--enable-shared \
|
||||
--enable-version3 \
|
||||
--prefix=/usr/local/ffmpeg \
|
||||
--toolchain=hardened \
|
||||
--disable-debug \
|
||||
--disable-devices \
|
||||
--disable-doc \
|
||||
--disable-ffplay \
|
||||
--disable-network \
|
||||
--disable-static \
|
||||
--enable-ffmpeg \
|
||||
--enable-ffprobe \
|
||||
--enable-gpl \
|
||||
--enable-libdav1d \
|
||||
--enable-libmp3lame \
|
||||
--enable-libopus \
|
||||
--enable-libsnappy \
|
||||
--enable-libvorbis \
|
||||
--enable-libvpx \
|
||||
--enable-libwebp \
|
||||
--enable-libx264 \
|
||||
--enable-libx265 \
|
||||
--enable-shared \
|
||||
--enable-version3 \
|
||||
; \
|
||||
make -j$(nproc); \
|
||||
make install;
|
||||
|
@ -267,58 +258,57 @@ ARG TARGETPLATFORM
|
|||
COPY Gemfile* /opt/mastodon/
|
||||
|
||||
RUN \
|
||||
# Mount Ruby Gem caches
|
||||
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
|
||||
# Configure bundle to prevent changes to Gemfile and Gemfile.lock
|
||||
# Mount Ruby Gem caches
|
||||
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
|
||||
# Configure bundle to prevent changes to Gemfile and Gemfile.lock
|
||||
bundle config set --global frozen "true"; \
|
||||
# Configure bundle to not cache downloaded Gems
|
||||
# Configure bundle to not cache downloaded Gems
|
||||
bundle config set --global cache_all "false"; \
|
||||
# Configure bundle to only process production Gems
|
||||
# Configure bundle to only process production Gems
|
||||
bundle config set --local without "development test"; \
|
||||
# Configure bundle to not warn about root user
|
||||
# Configure bundle to not warn about root user
|
||||
bundle config set silence_root_warning "true"; \
|
||||
# Download and install required Gems
|
||||
# Download and install required Gems
|
||||
bundle install -j"$(nproc)";
|
||||
|
||||
# Create temporary node specific build layer from build layer
|
||||
FROM build AS yarn
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Copy Node package configuration files into working directory
|
||||
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
|
||||
COPY streaming/package.json /opt/mastodon/streaming/
|
||||
COPY .yarn /opt/mastodon/.yarn
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||
# Install Node packages
|
||||
yarn workspaces focus --production @mastodon/mastodon;
|
||||
|
||||
# Create temporary assets build layer from build layer
|
||||
FROM build AS precompiler
|
||||
|
||||
# Copy Mastodon sources into precompiler layer
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Copy Mastodon sources into layer
|
||||
COPY . /opt/mastodon/
|
||||
|
||||
# Copy bundler and node packages from build layer to container
|
||||
COPY --from=yarn /opt/mastodon /opt/mastodon/
|
||||
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
||||
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
||||
# Copy libvips components to layer for precompiler
|
||||
# Copy Node.js binaries/libraries into layer
|
||||
COPY --from=node /usr/local/bin /usr/local/bin
|
||||
COPY --from=node /usr/local/lib /usr/local/lib
|
||||
|
||||
RUN \
|
||||
# Configure Corepack
|
||||
rm /usr/local/bin/yarn*; \
|
||||
corepack enable; \
|
||||
corepack prepare --activate;
|
||||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||
# Install Node.js packages
|
||||
yarn workspaces focus --production @mastodon/mastodon;
|
||||
|
||||
# Copy libvips components into layer for precompiler
|
||||
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
|
||||
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
# Copy bundler packages into layer for precompiler
|
||||
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
||||
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
||||
|
||||
RUN \
|
||||
ldconfig; \
|
||||
# Use Ruby on Rails to create Mastodon assets
|
||||
# Use Ruby on Rails to create Mastodon assets
|
||||
SECRET_KEY_BASE_DUMMY=1 \
|
||||
bundle exec rails assets:precompile; \
|
||||
# Cleanup temporary files
|
||||
# Cleanup temporary files
|
||||
rm -fr /opt/mastodon/tmp;
|
||||
|
||||
# Prep final Mastodon Ruby layer
|
||||
|
@ -328,49 +318,49 @@ ARG TARGETPLATFORM
|
|||
|
||||
# hadolint ignore=DL3008
|
||||
RUN \
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Mount Corepack and Yarn caches from Docker buildx caches
|
||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||
# Apt update install non-dev versions of necessary components
|
||||
# Mount Apt cache and lib directories from Docker buildx caches
|
||||
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
|
||||
# Mount Corepack and Yarn caches from Docker buildx caches
|
||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||
# Apt update install non-dev versions of necessary components
|
||||
apt-get install -y --no-install-recommends \
|
||||
libexpat1 \
|
||||
libglib2.0-0 \
|
||||
libicu72 \
|
||||
libidn12 \
|
||||
libpq5 \
|
||||
libreadline8 \
|
||||
libssl3 \
|
||||
libyaml-0-2 \
|
||||
libexpat1 \
|
||||
libglib2.0-0 \
|
||||
libicu72 \
|
||||
libidn12 \
|
||||
libpq5 \
|
||||
libreadline8 \
|
||||
libssl3 \
|
||||
libyaml-0-2 \
|
||||
# libvips components
|
||||
libcgif0 \
|
||||
libexif12 \
|
||||
libheif1/bookworm-backports \
|
||||
libimagequant0 \
|
||||
libjpeg62-turbo \
|
||||
liblcms2-2 \
|
||||
liborc-0.4-0 \
|
||||
libspng0 \
|
||||
libtiff6 \
|
||||
libwebp7 \
|
||||
libwebpdemux2 \
|
||||
libwebpmux3 \
|
||||
libcgif0 \
|
||||
libexif12 \
|
||||
libheif1/bookworm-backports \
|
||||
libimagequant0 \
|
||||
libjpeg62-turbo \
|
||||
liblcms2-2 \
|
||||
liborc-0.4-0 \
|
||||
libspng0 \
|
||||
libtiff6 \
|
||||
libwebp7 \
|
||||
libwebpdemux2 \
|
||||
libwebpmux3 \
|
||||
# ffmpeg components
|
||||
libdav1d6 \
|
||||
libmp3lame0 \
|
||||
libopencore-amrnb0 \
|
||||
libopencore-amrwb0 \
|
||||
libopus0 \
|
||||
libsnappy1v5 \
|
||||
libtheora0 \
|
||||
libvorbis0a \
|
||||
libvorbisenc2 \
|
||||
libvorbisfile3 \
|
||||
libvpx7 \
|
||||
libx264-164 \
|
||||
libx265-199 \
|
||||
libdav1d6 \
|
||||
libmp3lame0 \
|
||||
libopencore-amrnb0 \
|
||||
libopencore-amrwb0 \
|
||||
libopus0 \
|
||||
libsnappy1v5 \
|
||||
libtheora0 \
|
||||
libvorbis0a \
|
||||
libvorbisenc2 \
|
||||
libvorbisfile3 \
|
||||
libvpx7 \
|
||||
libx264-164 \
|
||||
libx265-199 \
|
||||
;
|
||||
|
||||
# Copy Mastodon sources into final layer
|
||||
|
@ -390,7 +380,7 @@ COPY --from=ffmpeg /usr/local/ffmpeg/lib /usr/local/lib
|
|||
|
||||
RUN \
|
||||
ldconfig; \
|
||||
# Smoketest media processors
|
||||
# Smoketest media processors
|
||||
vips -v; \
|
||||
ffmpeg -version; \
|
||||
ffprobe -version;
|
||||
|
@ -400,10 +390,10 @@ RUN \
|
|||
bundle exec bootsnap precompile --gemfile app/ lib/;
|
||||
|
||||
RUN \
|
||||
# Pre-create and chown system volume to Mastodon user
|
||||
# Pre-create and chown system volume to Mastodon user
|
||||
mkdir -p /opt/mastodon/public/system; \
|
||||
chown mastodon:mastodon /opt/mastodon/public/system; \
|
||||
# Set Mastodon user as owner of tmp folder
|
||||
# Set Mastodon user as owner of tmp folder
|
||||
chown -R mastodon:mastodon /opt/mastodon/tmp;
|
||||
|
||||
# Set the running user for resulting container
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md)
|
||||
- [FEP-8fcf: Followers collection synchronization across servers](https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md)
|
||||
- [FEP-5feb: Search indexing consent for actors](https://codeberg.org/fediverse/fep/src/branch/main/fep/5feb/fep-5feb.md)
|
||||
- [FEP-044f: Consent-respecting quote posts](https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md): partial support for incoming quote-posts
|
||||
|
||||
## ActivityPub in Mastodon
|
||||
|
||||
|
|
98
Gemfile
98
Gemfile
|
@ -1,12 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 3.1.0'
|
||||
ruby '>= 3.2.0', '< 3.5.0'
|
||||
|
||||
gem 'propshaft'
|
||||
gem 'puma', '~> 6.3'
|
||||
gem 'rack', '~> 2.2.7'
|
||||
gem 'rails', '~> 7.1.1'
|
||||
gem 'rails', '~> 8.0'
|
||||
gem 'thor', '~> 1.2'
|
||||
|
||||
gem 'dotenv'
|
||||
|
@ -14,18 +13,19 @@ gem 'haml-rails', '~>2.0'
|
|||
gem 'pg', '~> 1.5'
|
||||
gem 'pghero'
|
||||
|
||||
gem 'aws-sdk-core', '< 3.216.0', require: false # TODO: https://github.com/mastodon/mastodon/pull/34173#issuecomment-2733378873
|
||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||
gem 'blurhash', '~> 0.1'
|
||||
gem 'fog-core', '<= 2.5.0'
|
||||
gem 'fog-core', '<= 2.6.0'
|
||||
gem 'fog-openstack', '~> 1.0', require: false
|
||||
gem 'jd-paperclip-azure', '~> 3.0', require: false
|
||||
gem 'kt-paperclip', '~> 7.2'
|
||||
gem 'md-paperclip-azure', '~> 2.2', require: false
|
||||
gem 'ruby-vips', '~> 2.2', require: false
|
||||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', '~> 1.18.0', require: false
|
||||
gem 'browser', '< 6' # https://github.com/fnando/browser/issues/543
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy', '~> 7.3'
|
||||
gem 'devise', '~> 4.9'
|
||||
|
@ -39,7 +39,7 @@ gem 'net-ldap', '~> 0.18'
|
|||
|
||||
gem 'omniauth', '~> 2.0'
|
||||
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
||||
gem 'omniauth_openid_connect', '~> 0.6.1'
|
||||
gem 'omniauth_openid_connect', '~> 0.8.0'
|
||||
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
||||
gem 'omniauth-saml', '~> 2.0'
|
||||
|
||||
|
@ -47,21 +47,25 @@ gem 'color_diff', '~> 0.1'
|
|||
gem 'csv', '~> 3.2'
|
||||
gem 'discard', '~> 1.2'
|
||||
gem 'doorkeeper', '~> 5.6'
|
||||
gem 'faraday-httpclient'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'fastimage'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'hiredis-client'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 5.2.0'
|
||||
gem 'http', '~> 5.3.0'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 1.7.0'
|
||||
gem 'httplog', '~> 1.7.0', require: false
|
||||
gem 'i18n'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'inline_svg'
|
||||
gem 'irb', '~> 1.8'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'linzer', '~> 0.7.7'
|
||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
|
||||
gem 'mime-types', '~> 3.7.0', require: 'mime/types/columnar'
|
||||
gem 'mutex_m'
|
||||
gem 'nokogiri', '~> 1.15'
|
||||
gem 'oj', '~> 3.14'
|
||||
gem 'ox', '~> 2.14'
|
||||
|
@ -70,19 +74,18 @@ gem 'premailer-rails'
|
|||
gem 'public_suffix', '~> 6.0'
|
||||
gem 'pundit', '~> 2.3'
|
||||
gem 'rack-attack', '~> 6.6'
|
||||
gem 'rack-cors', '~> 2.0', require: 'rack/cors'
|
||||
gem 'rails-i18n', '~> 7.0'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
gem 'rails-i18n', '~> 8.0'
|
||||
gem 'redcarpet', '~> 3.6'
|
||||
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'redis-namespace', '~> 1.10'
|
||||
gem 'rqrcode', '~> 2.2'
|
||||
gem 'rqrcode', '~> 3.0'
|
||||
gem 'ruby-progressbar', '~> 1.13'
|
||||
gem 'sanitize', '~> 6.0'
|
||||
gem 'sanitize', '~> 7.0'
|
||||
gem 'scenic', '~> 1.7'
|
||||
gem 'sidekiq', '~> 6.5'
|
||||
gem 'sidekiq', '< 8'
|
||||
gem 'sidekiq-bulk', '~> 0.2.0'
|
||||
gem 'sidekiq-scheduler', '~> 5.0'
|
||||
gem 'sidekiq-unique-jobs', '~> 7.1'
|
||||
gem 'sidekiq-unique-jobs', '> 8'
|
||||
gem 'simple_form', '~> 5.2'
|
||||
gem 'simple-navigation', '~> 4.4'
|
||||
gem 'stoplight', '~> 4.1'
|
||||
|
@ -91,30 +94,31 @@ gem 'tty-prompt', '~> 0.23', require: false
|
|||
gem 'twitter-text', '~> 3.1.0'
|
||||
gem 'tzinfo-data', '~> 1.2023'
|
||||
gem 'webauthn', '~> 3.0'
|
||||
gem 'webpacker', '~> 5.4'
|
||||
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
|
||||
gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
|
||||
|
||||
gem 'json-ld'
|
||||
gem 'json-ld-preloaded', '~> 3.2'
|
||||
gem 'rdf-normalize', '~> 0.5'
|
||||
|
||||
gem 'opentelemetry-api', '~> 1.4.0'
|
||||
gem 'prometheus_exporter', '~> 2.2', require: false
|
||||
|
||||
gem 'opentelemetry-api', '~> 1.5.0'
|
||||
|
||||
group :opentelemetry do
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.29.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.31.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
|
||||
gem 'opentelemetry-exporter-otlp', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
|
||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
|
||||
gem 'opentelemetry-instrumentation-excon', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-faraday', '~> 0.27.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http', '~> 0.25.0', require: false
|
||||
gem 'opentelemetry-instrumentation-http_client', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
|
||||
gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-rails', '~> 0.36.0', require: false
|
||||
gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
|
||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||
end
|
||||
|
||||
|
@ -123,7 +127,7 @@ group :test do
|
|||
gem 'flatware-rspec'
|
||||
|
||||
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
|
||||
gem 'rspec-github', '~> 2.4', require: false
|
||||
gem 'rspec-github', '~> 3.0', require: false
|
||||
|
||||
# RSpec helpers for email specs
|
||||
gem 'email_spec'
|
||||
|
@ -133,7 +137,7 @@ group :test do
|
|||
|
||||
# Browser integration testing
|
||||
gem 'capybara', '~> 3.39'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'capybara-playwright-driver'
|
||||
|
||||
# Used to reset the database between system tests
|
||||
gem 'database_cleaner-active_record'
|
||||
|
@ -141,9 +145,6 @@ group :test do
|
|||
# Used to mock environment variables
|
||||
gem 'climate_control'
|
||||
|
||||
# Add back helpers functions removed in Rails 5.1
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
|
||||
# Validate schemas in specs
|
||||
gem 'json-schema', '~> 5.0'
|
||||
|
||||
|
@ -152,7 +153,7 @@ group :test do
|
|||
|
||||
gem 'shoulda-matchers'
|
||||
|
||||
# Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false
|
||||
# Coverage formatter for RSpec
|
||||
gem 'simplecov', '~> 0.22', require: false
|
||||
gem 'simplecov-lcov', '~> 0.8', require: false
|
||||
|
||||
|
@ -164,13 +165,14 @@ group :development do
|
|||
# Code linting CLI and plugins
|
||||
gem 'rubocop', require: false
|
||||
gem 'rubocop-capybara', require: false
|
||||
gem 'rubocop-i18n', require: false
|
||||
gem 'rubocop-performance', require: false
|
||||
gem 'rubocop-rails', require: false
|
||||
gem 'rubocop-rspec', require: false
|
||||
gem 'rubocop-rspec_rails', require: false
|
||||
|
||||
# Annotates modules with schema
|
||||
gem 'annotate', '~> 3.2'
|
||||
gem 'annotaterb', '~> 4.13', require: false
|
||||
|
||||
# Enhanced error message pages for development
|
||||
gem 'better_errors', '~> 2.9'
|
||||
|
@ -181,7 +183,7 @@ group :development do
|
|||
gem 'letter_opener_web', '~> 3.0'
|
||||
|
||||
# Security analysis CLI tools
|
||||
gem 'brakeman', '~> 6.0', require: false
|
||||
gem 'brakeman', '~> 7.0', require: false
|
||||
gem 'bundler-audit', '~> 0.9', require: false
|
||||
|
||||
# Linter CLI for HAML files
|
||||
|
@ -193,22 +195,22 @@ end
|
|||
|
||||
group :development, :test do
|
||||
# Interactive Debugging tools
|
||||
gem 'debug', '~> 1.8'
|
||||
gem 'debug', '~> 1.8', require: false
|
||||
|
||||
# Generate fake data values
|
||||
gem 'faker', '~> 3.2'
|
||||
|
||||
# Generate factory objects
|
||||
gem 'fabrication', '~> 2.30'
|
||||
gem 'fabrication'
|
||||
|
||||
# Profiling tools
|
||||
gem 'memory_profiler', require: false
|
||||
gem 'ruby-prof', require: false
|
||||
gem 'stackprof', require: false
|
||||
gem 'test-prof'
|
||||
gem 'test-prof', require: false
|
||||
|
||||
# RSpec runner for rails
|
||||
gem 'rspec-rails', '~> 7.0'
|
||||
gem 'rspec-rails', '~> 8.0'
|
||||
end
|
||||
|
||||
group :production do
|
||||
|
@ -220,9 +222,11 @@ gem 'concurrent-ruby', require: false
|
|||
gem 'connection_pool', require: false
|
||||
gem 'xorcist', '~> 1.1'
|
||||
|
||||
gem 'net-http', '~> 0.4.0'
|
||||
gem 'net-http', '~> 0.6.0'
|
||||
gem 'rubyzip', '~> 2.3'
|
||||
|
||||
gem 'hcaptcha', '~> 7.1'
|
||||
|
||||
gem 'mail', '~> 2.8'
|
||||
|
||||
gem 'vite_rails', '~> 3.0.19'
|
||||
|
|
911
Gemfile.lock
911
Gemfile.lock
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
||||
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
||||
stream: env PORT=4000 yarn workspace @mastodon/streaming start
|
||||
webpack: bin/webpack-dev-server
|
||||
vite: yarn dev
|
||||
|
|
187
README.md
187
README.md
|
@ -1,158 +1,99 @@
|
|||
<h1><picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./lib/assets/wordmark.dark.png?raw=true">
|
||||
<source media="(prefers-color-scheme: light)" srcset="./lib/assets/wordmark.light.png?raw=true">
|
||||
<img alt="Mastodon" src="./lib/assets/wordmark.light.png?raw=true" height="34">
|
||||
</picture></h1>
|
||||
> [!NOTE]
|
||||
> Want to learn more about Mastodon?
|
||||
> Click below to find out more in a video.
|
||||
|
||||
[][releases]
|
||||
[](https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml)
|
||||
[][crowdin]
|
||||
<p align="center">
|
||||
<a style="text-decoration:none" href="https://www.youtube.com/watch?v=IPSbNdBmWKE">
|
||||
<img alt="Mastodon hero image" src="https://github.com/user-attachments/assets/ef53f5e9-c0d8-484d-9f53-00efdebb92c3" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[releases]: https://github.com/mastodon/mastodon/releases
|
||||
[crowdin]: https://crowdin.com/project/mastodon
|
||||
<p align="center">
|
||||
<a style="text-decoration:none" href="https://github.com/mastodon/mastodon/releases">
|
||||
<img src="https://img.shields.io/github/release/mastodon/mastodon.svg" alt="Release" /></a>
|
||||
<a style="text-decoration:none" href="https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml">
|
||||
<img src="https://github.com/mastodon/mastodon/actions/workflows/test-ruby.yml/badge.svg" alt="Ruby Testing" /></a>
|
||||
<a style="text-decoration:none" href="https://crowdin.com/project/mastodon">
|
||||
<img src="https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg" alt="Crowdin" /></a>
|
||||
</p>
|
||||
|
||||
Mastodon is a **free, open-source social network server** based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
||||
|
||||
Click below to **learn more** in a video:
|
||||
|
||||
[][youtube_demo]
|
||||
|
||||
[youtube_demo]: https://www.youtube.com/watch?v=IPSbNdBmWKE
|
||||
Mastodon is a **free, open-source social network server** based on [ActivityPub](https://www.w3.org/TR/activitypub/) where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, and video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub!)
|
||||
|
||||
## Navigation
|
||||
|
||||
- [Project homepage 🐘](https://joinmastodon.org)
|
||||
- [Support the development via Patreon][patreon]
|
||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||
- [Blog](https://blog.joinmastodon.org)
|
||||
- [Documentation](https://docs.joinmastodon.org)
|
||||
- [Roadmap](https://joinmastodon.org/roadmap)
|
||||
- [Official Docker image](https://github.com/mastodon/mastodon/pkgs/container/mastodon)
|
||||
- [Browse Mastodon servers](https://joinmastodon.org/communities)
|
||||
- [Browse Mastodon apps](https://joinmastodon.org/apps)
|
||||
|
||||
[patreon]: https://www.patreon.com/mastodon
|
||||
- [Donate to support development 🎁](https://joinmastodon.org/sponsors#donate)
|
||||
- [View sponsors](https://joinmastodon.org/sponsors)
|
||||
- [Blog 📰](https://blog.joinmastodon.org)
|
||||
- [Documentation 📚](https://docs.joinmastodon.org)
|
||||
- [Official container image 🚢](https://github.com/mastodon/mastodon/pkgs/container/mastodon)
|
||||
|
||||
## Features
|
||||
|
||||
<img src="/app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
|
||||
<img src="./app/javascript/images/elephant_ui_working.svg?raw=true" align="right" width="30%" />
|
||||
|
||||
### No vendor lock-in: Fully interoperable with any conforming platform
|
||||
**Part of the Fediverse. Based on open standards, with no vendor lock-in.** - the network goes beyond just Mastodon; anything that implements ActivityPub is part of a broader social network known as [the Fediverse](https://jointhefediverse.net/). You can follow and interact with users on other servers (including those running different software), and they can follow you back.
|
||||
|
||||
It doesn't have to be Mastodon; whatever implements ActivityPub is part of the social network! [Learn more](https://blog.joinmastodon.org/2018/06/why-activitypub-is-the-future/)
|
||||
**Real-time, chronological timeline updates** - updates of people you're following appear in real-time in the UI.
|
||||
|
||||
### Real-time, chronological timeline updates
|
||||
**Media attachments** - upload and view images and videos attached to the updates. Videos with no audio track are treated like animated GIFs; normal videos loop continuously.
|
||||
|
||||
Updates of people you're following appear in real-time in the UI via WebSockets. There's a firehose view as well!
|
||||
**Safety and moderation tools** - Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and many other features, along with a reporting and moderation system.
|
||||
|
||||
### 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 loop continuously!
|
||||
|
||||
### Safety and moderation tools
|
||||
|
||||
Mastodon includes private posts, locked accounts, phrase filtering, muting, blocking, and all sorts of other features, along with a reporting and moderation system. [Learn more](https://blog.joinmastodon.org/2018/07/cage-the-mastodon/)
|
||||
|
||||
### OAuth2 and a straightforward REST API
|
||||
|
||||
Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Streaming APIs. This results in a rich app ecosystem with a lot of choices!
|
||||
**OAuth2 and a straightforward REST API** - Mastodon acts as an OAuth2 provider, and third party apps can use the REST and Streaming APIs. This results in a [rich app ecosystem](https://joinmastodon.org/apps) with a variety of choices!
|
||||
|
||||
## Deployment
|
||||
|
||||
### Tech stack
|
||||
|
||||
- **Ruby on Rails** powers the REST API and other web pages
|
||||
- **React.js** and **Redux** are used for the dynamic parts of the interface
|
||||
- **Node.js** powers the streaming API
|
||||
- [Ruby on Rails](https://github.com/rails/rails) powers the REST API and other web pages.
|
||||
- [PostgreSQL](https://www.postgresql.org/) is the main database.
|
||||
- [Redis](https://redis.io/) and [Sidekiq](https://sidekiq.org/) are used for caching and queueing.
|
||||
- [Node.js](https://nodejs.org/) powers the streaming API.
|
||||
- [React.js](https://reactjs.org/) and [Redux](https://redux.js.org/) are used for the dynamic parts of the interface.
|
||||
- [BrowserStack](https://www.browserstack.com/) supports testing on real devices and browsers. (This project is tested with BrowserStack)
|
||||
- [Chromatic](https://www.chromatic.com/) provides visual regression testing. (This project is tested with Chromatic)
|
||||
|
||||
### Requirements
|
||||
|
||||
- **PostgreSQL** 12+
|
||||
- **Redis** 4+
|
||||
- **Ruby** 3.1+
|
||||
- **Node.js** 18+
|
||||
- **Ruby** 3.2+
|
||||
- **PostgreSQL** 13+
|
||||
- **Redis** 6.2+
|
||||
- **Node.js** 20+
|
||||
|
||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||
|
||||
## Development
|
||||
|
||||
### Vagrant
|
||||
|
||||
A **Vagrant** configuration is included for development purposes. To use it, complete the following steps:
|
||||
|
||||
- Install Vagrant and Virtualbox
|
||||
- Install the `vagrant-hostsupdater` plugin: `vagrant plugin install vagrant-hostsupdater`
|
||||
- Run `vagrant up`
|
||||
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
|
||||
- Open `http://mastodon.local` in your browser
|
||||
|
||||
### macOS
|
||||
|
||||
To set up **macOS** for native development, complete the following steps:
|
||||
|
||||
- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick
|
||||
libidn nvm` to install the required project dependencies
|
||||
- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
|
||||
`nvm use` to activate the node version from `.nvmrc`
|
||||
- Run the `bin/setup` script, which will install the required ruby gems and node
|
||||
packages and prepare the database for local development
|
||||
- Finally, run the `bin/dev` script which will launch services via `overmind`
|
||||
(if installed) or `foreman`
|
||||
|
||||
### Docker
|
||||
|
||||
For production hosting and deployment with **Docker**, use the `Dockerfile` and
|
||||
`docker-compose.yml` in the project root directory.
|
||||
|
||||
For local development, install and launch [Docker], and run:
|
||||
|
||||
```shell
|
||||
docker compose -f .devcontainer/compose.yaml up -d
|
||||
docker compose -f .devcontainer/compose.yaml exec app bin/setup
|
||||
docker compose -f .devcontainer/compose.yaml exec app bin/dev
|
||||
```
|
||||
|
||||
### Dev Containers
|
||||
|
||||
Within IDEs that support the [Development Containers] specification, start the
|
||||
"Mastodon on local machine" container from the editor. The necessary `docker
|
||||
compose` commands to build and setup the container should run automatically. For
|
||||
**Visual Studio Code** this requires installing the [Dev Container extension].
|
||||
|
||||
### GitHub Codespaces
|
||||
|
||||
[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
|
||||
development environment configured with the software needed for this project.
|
||||
|
||||
[][codespace]
|
||||
|
||||
- Click the button to create a new codespace, and confirm the options
|
||||
- Wait for the environment to build (takes a few minutes)
|
||||
- When the editor is ready, run `bin/dev` in the terminal
|
||||
- Wait for an _Open in Browser_ prompt. This will open Mastodon
|
||||
- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
|
||||
This repository includes deployment configurations for **Docker and docker-compose**, as well as for other environments like Heroku and Scalingo. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). A [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the main documentation.
|
||||
|
||||
## Contributing
|
||||
|
||||
Mastodon is **free, open-source software** licensed under **AGPLv3**.
|
||||
Mastodon is **free, open-source software** licensed under **AGPLv3**. We welcome contributions and help from anyone who wants to improve the project.
|
||||
|
||||
You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository or submit translations using Crowdin. To get started, take a look at [CONTRIBUTING.md](CONTRIBUTING.md). If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon).
|
||||
You should read the overall [CONTRIBUTING](https://github.com/mastodon/.github/blob/main/CONTRIBUTING.md) guide, which covers our development processes.
|
||||
|
||||
**IRC channel**: #mastodon on irc.libera.chat
|
||||
You should also read and understand the [CODE OF CONDUCT](https://github.com/mastodon/.github/blob/main/CODE_OF_CONDUCT.md) that enables us to maintain a welcoming and inclusive community. Collaboration begins with mutual respect and understanding.
|
||||
|
||||
## License
|
||||
You can learn about setting up a development environment in the [DEVELOPMENT](docs/DEVELOPMENT.md) documentation.
|
||||
|
||||
Copyright (C) 2016-2024 Eugen Rochko & other Mastodon contributors (see [AUTHORS.md](AUTHORS.md))
|
||||
If you would like to help with translations 🌐 you can do so on [Crowdin](https://crowdin.com/project/mastodon).
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
## LICENSE
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
Copyright (c) 2016-2025 Eugen Rochko (+ [`mastodon authors`](AUTHORS.md))
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Licensed under GNU Affero General Public License as stated in the [LICENSE](LICENSE):
|
||||
|
||||
[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json
|
||||
[Dev Container extension]: https://containers.dev/supporting#dev-containers
|
||||
[Development Containers]: https://containers.dev/supporting
|
||||
[Docker]: https://docs.docker.com
|
||||
[GitHub Codespaces]: https://docs.github.com/en/codespaces
|
||||
[Homebrew]: https://brew.sh
|
||||
```text
|
||||
Copyright (c) 2016-2025 Eugen Rochko & other Mastodon contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along
|
||||
with this program. If not, see https://www.gnu.org/licenses/
|
||||
```
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -3,6 +3,6 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require File.expand_path('config/application', __dir__)
|
||||
require_relative 'config/application'
|
||||
|
||||
Rails.application.load_tasks
|
||||
|
|
|
@ -15,7 +15,7 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
|
|||
|
||||
| Version | Supported |
|
||||
| ------- | ---------------- |
|
||||
| 4.4.x | Yes |
|
||||
| 4.3.x | Yes |
|
||||
| 4.2.x | Yes |
|
||||
| 4.1.x | Until 2025-04-08 |
|
||||
| < 4.1 | No |
|
||||
| 4.2.x | Until 2026-01-08 |
|
||||
| < 4.2 | No |
|
||||
|
|
2
Vagrantfile
vendored
2
Vagrantfile
vendored
|
@ -174,7 +174,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
if config.vm.networks.any? { |type, options| type == :private_network }
|
||||
config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1']
|
||||
else
|
||||
config.vm.synced_folder ".", "/vagrant"
|
||||
config.vm.synced_folder ".", "/vagrant", type: "rsync", create: true, rsync__args: ["--verbose", "--archive", "--delete", "-z"]
|
||||
end
|
||||
|
||||
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080
|
||||
|
|
4
app.json
4
app.json
|
@ -17,10 +17,6 @@
|
|||
"description": "The secret key base",
|
||||
"generator": "secret"
|
||||
},
|
||||
"OTP_SECRET": {
|
||||
"description": "One-time password secret",
|
||||
"generator": "secret"
|
||||
},
|
||||
"SINGLE_USER_MODE": {
|
||||
"description": "Should the instance run in single user mode? (Disable registrations, redirect to front page)",
|
||||
"value": "false",
|
||||
|
|
|
@ -19,9 +19,16 @@ class AccountsIndex < Chewy::Index
|
|||
type: 'stemmer',
|
||||
language: 'possessive_english',
|
||||
},
|
||||
|
||||
word_joiner: {
|
||||
type: 'shingle',
|
||||
output_unigrams: true,
|
||||
token_separator: '',
|
||||
},
|
||||
},
|
||||
|
||||
analyzer: {
|
||||
# "The FOOING's bar" becomes "foo bar"
|
||||
natural: {
|
||||
tokenizer: 'standard',
|
||||
filter: %w(
|
||||
|
@ -35,11 +42,20 @@ class AccountsIndex < Chewy::Index
|
|||
),
|
||||
},
|
||||
|
||||
# "FOO bar" becomes "foo bar"
|
||||
verbatim: {
|
||||
tokenizer: 'standard',
|
||||
filter: %w(lowercase asciifolding cjk_width),
|
||||
},
|
||||
|
||||
# "Foo bar" becomes "foo bar foobar"
|
||||
word_join_analyzer: {
|
||||
type: 'custom',
|
||||
tokenizer: 'standard',
|
||||
filter: %w(lowercase asciifolding cjk_width word_joiner),
|
||||
},
|
||||
|
||||
# "Foo bar" becomes "f fo foo b ba bar"
|
||||
edge_ngram: {
|
||||
tokenizer: 'edge_ngram',
|
||||
filter: %w(lowercase asciifolding cjk_width),
|
||||
|
|
|
@ -49,7 +49,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
|
||||
def collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_collection_url(@account, params[:id]),
|
||||
id: ActivityPub::TagManager.instance.collection_uri_for(@account, params[:id]),
|
||||
type: @type,
|
||||
size: @size,
|
||||
items: @items
|
||||
|
|
|
@ -41,12 +41,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
end
|
||||
end
|
||||
|
||||
def outbox_url(**kwargs)
|
||||
if params[:account_username].present?
|
||||
account_outbox_url(@account, **kwargs)
|
||||
else
|
||||
instance_actor_outbox_url(**kwargs)
|
||||
end
|
||||
def outbox_url(...)
|
||||
ActivityPub::TagManager.instance.outbox_uri_for(@account, ...)
|
||||
end
|
||||
|
||||
def next_page
|
||||
|
|
|
@ -14,16 +14,20 @@ module Admin
|
|||
def create
|
||||
authorize @account, :show?
|
||||
|
||||
account_action = Admin::AccountAction.new(resource_params)
|
||||
account_action.target_account = @account
|
||||
account_action.current_account = current_account
|
||||
@account_action = Admin::AccountAction.new(resource_params)
|
||||
@account_action.target_account = @account
|
||||
@account_action.current_account = current_account
|
||||
|
||||
account_action.save!
|
||||
|
||||
if account_action.with_report?
|
||||
redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: resource_params[:report_id])
|
||||
if @account_action.save
|
||||
if @account_action.with_report?
|
||||
redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: resource_params[:report_id])
|
||||
else
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
else
|
||||
redirect_to admin_account_path(@account.id)
|
||||
@warning_presets = AccountWarningPreset.all
|
||||
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -34,7 +38,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses)
|
||||
params
|
||||
.expect(admin_account_action: [:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,10 +29,8 @@ module Admin
|
|||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:account_moderation_note).permit(
|
||||
:content,
|
||||
:target_account_id
|
||||
)
|
||||
params
|
||||
.expect(account_moderation_note: [:content, :target_account_id])
|
||||
end
|
||||
|
||||
def set_account_moderation_note
|
||||
|
|
|
@ -158,7 +158,8 @@ module Admin
|
|||
end
|
||||
|
||||
def form_account_batch_params
|
||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
||||
params
|
||||
.expect(form_account_batch: [:action, account_ids: []])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Announcements::DistributionsController < Admin::BaseController
|
||||
before_action :set_announcement
|
||||
|
||||
def create
|
||||
authorize @announcement, :distribute?
|
||||
@announcement.touch(:notification_sent_at)
|
||||
Admin::DistributeAnnouncementNotificationWorker.perform_async(@announcement.id)
|
||||
redirect_to admin_announcements_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_announcement
|
||||
@announcement = Announcement.find(params[:announcement_id])
|
||||
end
|
||||
end
|
16
app/controllers/admin/announcements/previews_controller.rb
Normal file
16
app/controllers/admin/announcements/previews_controller.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Announcements::PreviewsController < Admin::BaseController
|
||||
before_action :set_announcement
|
||||
|
||||
def show
|
||||
authorize @announcement, :distribute?
|
||||
@user_count = @announcement.scope_for_notification.count
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_announcement
|
||||
@announcement = Announcement.find(params[:announcement_id])
|
||||
end
|
||||
end
|
17
app/controllers/admin/announcements/tests_controller.rb
Normal file
17
app/controllers/admin/announcements/tests_controller.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Announcements::TestsController < Admin::BaseController
|
||||
before_action :set_announcement
|
||||
|
||||
def create
|
||||
authorize @announcement, :distribute?
|
||||
UserMailer.announcement_published(current_user, @announcement).deliver_later!
|
||||
redirect_to admin_announcements_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_announcement
|
||||
@announcement = Announcement.find(params[:announcement_id])
|
||||
end
|
||||
end
|
|
@ -84,6 +84,7 @@ class Admin::AnnouncementsController < Admin::BaseController
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:announcement).permit(:text, :scheduled_at, :starts_at, :ends_at, :all_day)
|
||||
params
|
||||
.expect(announcement: [:text, :scheduled_at, :starts_at, :ends_at, :all_day])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,14 @@ module Admin
|
|||
|
||||
layout 'admin'
|
||||
|
||||
before_action :set_cache_headers
|
||||
before_action :set_referrer_policy_header
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
private
|
||||
|
||||
def set_cache_headers
|
||||
response.cache_control.replace(private: true, no_store: true)
|
||||
def set_referrer_policy_header
|
||||
response.headers['Referrer-Policy'] = 'same-origin'
|
||||
end
|
||||
|
||||
def set_user
|
||||
|
|
|
@ -41,9 +41,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user).permit(
|
||||
:unconfirmed_email
|
||||
)
|
||||
params
|
||||
.expect(user: [:unconfirmed_email])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,7 +44,8 @@ module Admin
|
|||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
|
||||
params
|
||||
.expect(custom_emoji: [:shortcode, :image, :visible_in_picker])
|
||||
end
|
||||
|
||||
def filtered_custom_emojis
|
||||
|
@ -74,7 +75,8 @@ module Admin
|
|||
end
|
||||
|
||||
def form_custom_emoji_batch_params
|
||||
params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: [])
|
||||
params
|
||||
.expect(form_custom_emoji_batch: [:action, :category_id, :category_name, custom_emoji_ids: []])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,6 +37,7 @@ class Admin::DomainAllowsController < Admin::BaseController
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_allow).permit(:domain)
|
||||
params
|
||||
.expect(domain_allow: [:domain])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,7 +25,9 @@ module Admin
|
|||
rescue Mastodon::NotPermittedError
|
||||
flash[:alert] = I18n.t('admin.domain_blocks.not_permitted')
|
||||
else
|
||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
||||
flash[:notice] = I18n.t('admin.domain_blocks.created_msg')
|
||||
ensure
|
||||
redirect_to admin_instances_path(limited: '1')
|
||||
end
|
||||
|
||||
def new
|
||||
|
@ -114,7 +116,12 @@ module Admin
|
|||
end
|
||||
|
||||
def form_domain_block_batch_params
|
||||
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate])
|
||||
params
|
||||
.expect(
|
||||
form_domain_block_batch: [
|
||||
domain_blocks_attributes: [[:enabled, :domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate]],
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
|
|
|
@ -5,7 +5,7 @@ module Admin
|
|||
def index
|
||||
authorize :email_domain_block, :index?
|
||||
|
||||
@email_domain_blocks = EmailDomainBlock.where(parent_id: nil).includes(:children).order(id: :desc).page(params[:page])
|
||||
@email_domain_blocks = EmailDomainBlock.parents.includes(:children).order(id: :desc).page(params[:page])
|
||||
@form = Form::EmailDomainBlockBatch.new
|
||||
end
|
||||
|
||||
|
@ -58,18 +58,17 @@ module Admin
|
|||
private
|
||||
|
||||
def set_resolved_records
|
||||
Resolv::DNS.open do |dns|
|
||||
dns.timeouts = 5
|
||||
@resolved_records = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a
|
||||
end
|
||||
@resolved_records = DomainResource.new(@email_domain_block.domain).mx
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:email_domain_block).permit(:domain, :allow_with_approval, other_domains: [])
|
||||
params
|
||||
.expect(email_domain_block: [:domain, :allow_with_approval, other_domains: []])
|
||||
end
|
||||
|
||||
def form_email_domain_block_batch_params
|
||||
params.require(:form_email_domain_block_batch).permit(email_domain_block_ids: [])
|
||||
params
|
||||
.expect(form_email_domain_block_batch: [email_domain_block_ids: []])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
|
|
20
app/controllers/admin/fasp/debug/callbacks_controller.rb
Normal file
20
app/controllers/admin/fasp/debug/callbacks_controller.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Fasp::Debug::CallbacksController < Admin::BaseController
|
||||
def index
|
||||
authorize [:admin, :fasp, :provider], :update?
|
||||
|
||||
@callbacks = Fasp::DebugCallback
|
||||
.includes(:fasp_provider)
|
||||
.order(created_at: :desc)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize [:admin, :fasp, :provider], :update?
|
||||
|
||||
callback = Fasp::DebugCallback.find(params[:id])
|
||||
callback.destroy
|
||||
|
||||
redirect_to admin_fasp_debug_callbacks_path
|
||||
end
|
||||
end
|
19
app/controllers/admin/fasp/debug_calls_controller.rb
Normal file
19
app/controllers/admin/fasp/debug_calls_controller.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Fasp::DebugCallsController < Admin::BaseController
|
||||
before_action :set_provider
|
||||
|
||||
def create
|
||||
authorize [:admin, @provider], :update?
|
||||
|
||||
@provider.perform_debug_call
|
||||
|
||||
redirect_to admin_fasp_providers_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_provider
|
||||
@provider = Fasp::Provider.find(params[:provider_id])
|
||||
end
|
||||
end
|
47
app/controllers/admin/fasp/providers_controller.rb
Normal file
47
app/controllers/admin/fasp/providers_controller.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Fasp::ProvidersController < Admin::BaseController
|
||||
before_action :set_provider, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
authorize [:admin, :fasp, :provider], :index?
|
||||
|
||||
@providers = Fasp::Provider.order(confirmed: :asc, created_at: :desc)
|
||||
end
|
||||
|
||||
def show
|
||||
authorize [:admin, @provider], :show?
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize [:admin, @provider], :update?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize [:admin, @provider], :update?
|
||||
|
||||
if @provider.update(provider_params)
|
||||
redirect_to admin_fasp_providers_path
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize [:admin, @provider], :destroy?
|
||||
|
||||
@provider.destroy
|
||||
|
||||
redirect_to admin_fasp_providers_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def provider_params
|
||||
params.expect(fasp_provider: [capabilities_attributes: {}])
|
||||
end
|
||||
|
||||
def set_provider
|
||||
@provider = Fasp::Provider.find(params[:id])
|
||||
end
|
||||
end
|
23
app/controllers/admin/fasp/registrations_controller.rb
Normal file
23
app/controllers/admin/fasp/registrations_controller.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Fasp::RegistrationsController < Admin::BaseController
|
||||
before_action :set_provider
|
||||
|
||||
def new
|
||||
authorize [:admin, @provider], :create?
|
||||
end
|
||||
|
||||
def create
|
||||
authorize [:admin, @provider], :create?
|
||||
|
||||
@provider.update_info!(confirm: true)
|
||||
|
||||
redirect_to edit_admin_fasp_provider_path(@provider)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_provider
|
||||
@provider = Fasp::Provider.find(params[:provider_id])
|
||||
end
|
||||
end
|
|
@ -37,7 +37,8 @@ module Admin
|
|||
end
|
||||
|
||||
def form_account_batch_params
|
||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
||||
params
|
||||
.expect(form_account_batch: [:action, account_ids: []])
|
||||
end
|
||||
|
||||
def filter_params
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Instances::ModerationNotesController < Admin::BaseController
|
||||
before_action :set_instance, only: [:create]
|
||||
before_action :set_instance_note, only: [:destroy]
|
||||
|
||||
def create
|
||||
authorize :instance_moderation_note, :create?
|
||||
|
||||
@instance_moderation_note = current_account.instance_moderation_notes.new(content: resource_params[:content], domain: @instance.domain)
|
||||
|
||||
if @instance_moderation_note.save
|
||||
redirect_to admin_instance_path(@instance.domain, anchor: helpers.dom_id(@instance_moderation_note)), notice: I18n.t('admin.instances.moderation_notes.created_msg')
|
||||
else
|
||||
@instance_moderation_notes = @instance.moderation_notes.includes(:account).chronological
|
||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(5)
|
||||
|
||||
render 'admin/instances/show'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @instance_moderation_note, :destroy?
|
||||
@instance_moderation_note.destroy!
|
||||
redirect_to admin_instance_path(@instance_moderation_note.domain, anchor: 'instance-notes'), notice: I18n.t('admin.instances.moderation_notes.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params
|
||||
.expect(instance_moderation_note: [:content])
|
||||
end
|
||||
|
||||
def set_instance
|
||||
domain = params[:instance_id]&.strip
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(domain))
|
||||
end
|
||||
|
||||
def set_instance_note
|
||||
@instance_moderation_note = InstanceModerationNote.find(params[:id])
|
||||
end
|
||||
end
|
|
@ -5,6 +5,8 @@ module Admin
|
|||
before_action :set_instances, only: :index
|
||||
before_action :set_instance, except: :index
|
||||
|
||||
LOGS_LIMIT = 5
|
||||
|
||||
def index
|
||||
authorize :instance, :index?
|
||||
preload_delivery_failures!
|
||||
|
@ -12,8 +14,11 @@ module Admin
|
|||
|
||||
def show
|
||||
authorize :instance, :show?
|
||||
|
||||
@instance_moderation_note = @instance.moderation_notes.new
|
||||
@instance_moderation_notes = @instance.moderation_notes.includes(:account).chronological
|
||||
@time_period = (6.days.ago.to_date...Time.now.utc.to_date)
|
||||
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(5)
|
||||
@action_logs = Admin::ActionLogFilter.new(target_domain: @instance.domain).results.limit(LOGS_LIMIT)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -50,7 +55,8 @@ module Admin
|
|||
private
|
||||
|
||||
def set_instance
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(params[:id]&.strip))
|
||||
domain = params[:id]&.strip
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(domain))
|
||||
end
|
||||
|
||||
def set_instances
|
||||
|
|
|
@ -32,14 +32,15 @@ module Admin
|
|||
|
||||
def deactivate_all
|
||||
authorize :invite, :deactivate_all?
|
||||
Invite.available.in_batches.update_all(expires_at: Time.now.utc)
|
||||
Invite.available.in_batches.touch_all(:expires_at)
|
||||
redirect_to admin_invites_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:invite).permit(:max_uses, :expires_in)
|
||||
params
|
||||
.expect(invite: [:max_uses, :expires_in])
|
||||
end
|
||||
|
||||
def filtered_invites
|
||||
|
|
|
@ -44,7 +44,8 @@ module Admin
|
|||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in)
|
||||
params
|
||||
.expect(ip_block: [:ip, :severity, :comment, :expires_in])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
|
@ -52,7 +53,8 @@ module Admin
|
|||
end
|
||||
|
||||
def form_ip_block_batch_params
|
||||
params.require(:form_ip_block_batch).permit(ip_block_ids: [])
|
||||
params
|
||||
.expect(form_ip_block_batch: [ip_block_ids: []])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,6 +21,7 @@ module Admin
|
|||
@relay = Relay.new(resource_params)
|
||||
|
||||
if @relay.save
|
||||
log_action :create, @relay
|
||||
@relay.enable!
|
||||
redirect_to admin_relays_path
|
||||
else
|
||||
|
@ -31,18 +32,21 @@ module Admin
|
|||
def destroy
|
||||
authorize :relay, :update?
|
||||
@relay.destroy
|
||||
log_action :destroy, @relay
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
def enable
|
||||
authorize :relay, :update?
|
||||
@relay.enable!
|
||||
log_action :enable, @relay
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
def disable
|
||||
authorize :relay, :update?
|
||||
@relay.disable!
|
||||
log_action :disable, @relay
|
||||
redirect_to admin_relays_path
|
||||
end
|
||||
|
||||
|
@ -53,7 +57,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:relay).permit(:inbox_url)
|
||||
params
|
||||
.expect(relay: [:inbox_url])
|
||||
end
|
||||
|
||||
def warn_signatures_not_enabled!
|
||||
|
|
|
@ -47,10 +47,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:report_note).permit(
|
||||
:content,
|
||||
:report_id
|
||||
)
|
||||
params
|
||||
.expect(report_note: [:content, :report_id])
|
||||
end
|
||||
|
||||
def set_report_note
|
||||
|
|
|
@ -61,7 +61,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user_role).permit(:name, :color, :highlighted, :position, permissions_as_keys: [])
|
||||
params
|
||||
.expect(user_role: [:name, :color, :highlighted, :position, permissions_as_keys: []])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,17 +2,24 @@
|
|||
|
||||
module Admin
|
||||
class RulesController < BaseController
|
||||
before_action :set_rule, except: [:index, :create]
|
||||
before_action :set_rule, except: [:index, :new, :create]
|
||||
|
||||
def index
|
||||
authorize :rule, :index?
|
||||
|
||||
@rules = Rule.ordered
|
||||
@rule = Rule.new
|
||||
@rules = Rule.ordered.includes(:translations)
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :rule, :create?
|
||||
@rule = Rule.new
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @rule, :update?
|
||||
|
||||
missing_languages = RuleTranslation.languages - @rule.translations.pluck(:language)
|
||||
missing_languages.each { |lang| @rule.translations.build(language: lang) }
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -23,8 +30,7 @@ module Admin
|
|||
if @rule.save
|
||||
redirect_to admin_rules_path
|
||||
else
|
||||
@rules = Rule.ordered
|
||||
render :index
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -46,6 +52,22 @@ module Admin
|
|||
redirect_to admin_rules_path
|
||||
end
|
||||
|
||||
def move_up
|
||||
authorize @rule, :update?
|
||||
|
||||
@rule.move!(-1)
|
||||
|
||||
redirect_to admin_rules_path
|
||||
end
|
||||
|
||||
def move_down
|
||||
authorize @rule, :update?
|
||||
|
||||
@rule.move!(+1)
|
||||
|
||||
redirect_to admin_rules_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_rule
|
||||
|
@ -53,7 +75,8 @@ module Admin
|
|||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:rule).permit(:text, :hint, :priority)
|
||||
params
|
||||
.expect(rule: [:text, :hint, :priority, translations_attributes: [[:id, :language, :text, :hint, :_destroy]]])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,8 @@ module Admin
|
|||
end
|
||||
|
||||
def settings_params
|
||||
params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS)
|
||||
params
|
||||
.expect(form_admin_settings: [*Form::AdminSettings::KEYS])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Admin
|
|||
|
||||
def index
|
||||
authorize :software_update, :index?
|
||||
@software_updates = SoftwareUpdate.all.sort_by(&:gem_version)
|
||||
@software_updates = SoftwareUpdate.by_version.filter(&:pending?)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -16,6 +16,8 @@ module Admin
|
|||
|
||||
def show
|
||||
authorize [:admin, @status], :show?
|
||||
|
||||
@status_batch_action = Admin::StatusBatchAction.new
|
||||
end
|
||||
|
||||
def batch
|
||||
|
@ -37,7 +39,8 @@ module Admin
|
|||
helper_method :batched_ordered_status_edits
|
||||
|
||||
def admin_status_batch_action_params
|
||||
params.require(:admin_status_batch_action).permit(status_ids: [])
|
||||
params
|
||||
.expect(admin_status_batch_action: [status_ids: []])
|
||||
end
|
||||
|
||||
def after_create_redirect_path
|
||||
|
|
|
@ -37,7 +37,8 @@ module Admin
|
|||
end
|
||||
|
||||
def tag_params
|
||||
params.require(:tag).permit(:name, :display_name, :trendable, :usable, :listable)
|
||||
params
|
||||
.expect(tag: [:name, :display_name, :trendable, :usable, :listable])
|
||||
end
|
||||
|
||||
def filtered_tags
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::DistributionsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def create
|
||||
authorize @terms_of_service, :distribute?
|
||||
@terms_of_service.touch(:notification_sent_at)
|
||||
Admin::DistributeTermsOfServiceNotificationWorker.perform_async(@terms_of_service.id)
|
||||
redirect_to admin_terms_of_service_index_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
|
||||
end
|
||||
end
|
37
app/controllers/admin/terms_of_service/drafts_controller.rb
Normal file
37
app/controllers/admin/terms_of_service/drafts_controller.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::DraftsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def show
|
||||
authorize :terms_of_service, :create?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @terms_of_service, :update?
|
||||
|
||||
@terms_of_service.published_at = Time.now.utc if params[:action_type] == 'publish'
|
||||
|
||||
if @terms_of_service.update(resource_params)
|
||||
log_action(:publish, @terms_of_service) if @terms_of_service.published?
|
||||
redirect_to @terms_of_service.published? ? admin_terms_of_service_index_path : admin_terms_of_service_draft_path
|
||||
else
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.draft.first || TermsOfService.new(text: current_terms_of_service&.text, effective_date: 10.days.from_now)
|
||||
end
|
||||
|
||||
def current_terms_of_service
|
||||
TermsOfService.live.first
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params
|
||||
.expect(terms_of_service: [:text, :changelog, :effective_date])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::GeneratesController < Admin::BaseController
|
||||
before_action :set_instance_presenter
|
||||
|
||||
def show
|
||||
authorize :terms_of_service, :create?
|
||||
|
||||
@generator = TermsOfService::Generator.new(
|
||||
domain: @instance_presenter.domain,
|
||||
admin_email: @instance_presenter.contact.email
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :terms_of_service, :create?
|
||||
|
||||
@generator = TermsOfService::Generator.new(resource_params)
|
||||
|
||||
if @generator.valid?
|
||||
TermsOfService.create!(text: @generator.render)
|
||||
redirect_to admin_terms_of_service_draft_path
|
||||
else
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params
|
||||
.expect(terms_of_service_generator: [*TermsOfService::Generator::VARIABLES])
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::HistoriesController < Admin::BaseController
|
||||
def show
|
||||
authorize :terms_of_service, :index?
|
||||
@terms_of_service = TermsOfService.published.all
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::PreviewsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def show
|
||||
authorize @terms_of_service, :distribute?
|
||||
@user_count = @terms_of_service.scope_for_notification.count
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
|
||||
end
|
||||
end
|
17
app/controllers/admin/terms_of_service/tests_controller.rb
Normal file
17
app/controllers/admin/terms_of_service/tests_controller.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::TermsOfService::TestsController < Admin::BaseController
|
||||
before_action :set_terms_of_service
|
||||
|
||||
def create
|
||||
authorize @terms_of_service, :distribute?
|
||||
UserMailer.terms_of_service_changed(current_user, @terms_of_service).deliver_later!
|
||||
redirect_to admin_terms_of_service_preview_path(@terms_of_service)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_terms_of_service
|
||||
@terms_of_service = TermsOfService.find(params[:terms_of_service_id])
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user