Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba8a221833 | ||
|
|
3f73cd9443 | ||
|
|
7a0a9dbd92 | ||
|
|
441c4fa6a9 | ||
|
|
a16169f69d | ||
|
|
da42676656 | ||
|
|
f3a42e892f | ||
|
|
6a53597485 | ||
|
|
60336e567d | ||
|
|
8918c62ffd | ||
|
|
362fc19cd8 | ||
|
|
1b938752ff | ||
|
|
835a886dc8 | ||
|
|
f390b57fe7 | ||
|
|
6d21a0f204 | ||
|
|
a1d0c37828 | ||
|
|
763fbc417e | ||
|
|
776d5e66fe | ||
|
|
10c0975ce0 | ||
|
|
7286642fc9 | ||
|
|
8db4fb3524 | ||
|
|
08be04979d | ||
|
|
d7ea9ba43f | ||
|
|
bad248534c | ||
|
|
f56ac4eabc | ||
|
|
bbe2e8416c | ||
|
|
d44e79b1f4 | ||
|
|
e816dd686c | ||
|
|
c9ea8f6162 | ||
|
|
87102cd3b0 | ||
|
|
8b6bab828d | ||
|
|
9fb658599a | ||
|
|
f8c33003a8 | ||
|
|
b97136b83f | ||
|
|
df3e983e7f | ||
|
|
1b878fb3cc | ||
|
|
aa87ab94f5 | ||
|
|
4e9afd9761 | ||
|
|
2b7d0ecb72 | ||
|
|
603c7d9439 | ||
|
|
fb93231b35 | ||
|
|
fed83263e2 | ||
|
|
9dc2d83486 | ||
|
|
d918caaf81 | ||
|
|
d44212bd90 | ||
|
|
37d4ca76fa | ||
|
|
8cc39f322c | ||
|
|
2b4e80651a | ||
|
|
415fa0f2e3 | ||
|
|
91809002d7 | ||
|
|
dcaad1b2d5 | ||
|
|
5e635bba68 | ||
|
|
d136d086eb | ||
|
|
9fe765f874 | ||
|
|
72317ead34 | ||
|
|
75c7914fb1 | ||
|
|
863b4deec0 | ||
|
|
6e6fd8d587 | ||
|
|
1608911328 | ||
|
|
87a62961b6 | ||
|
|
e6a9748e70 | ||
|
|
0f35532f0b | ||
|
|
d21e47f881 | ||
|
|
8d11a239fa | ||
|
|
8867eb296c | ||
|
|
a53ddc9f76 | ||
|
|
5ee9e76dcf | ||
|
|
3d5f77ab35 | ||
|
|
25a4f48e5e | ||
|
|
2dc3bc8934 | ||
|
|
7b1bb25d6a | ||
|
|
9bed5be236 | ||
|
|
ec0ef12b0e | ||
|
|
5d2950f842 | ||
|
|
25c2f30165 | ||
|
|
d8b49773b3 | ||
|
|
2c8107a4b7 | ||
|
|
9f8aba068e | ||
|
|
e40235b1c7 | ||
|
|
e050544d21 | ||
|
|
da692649bc | ||
|
|
a0892fc4c0 | ||
|
|
c1e365fdfd | ||
|
|
e6e6d2aa54 | ||
|
|
ac70cd4c9d | ||
|
|
600030b528 | ||
|
|
4e1537b4f0 | ||
|
|
8a8aac945d | ||
|
|
89be748dee | ||
|
|
6c35fe9ded | ||
|
|
9ea6e8d981 | ||
|
|
2c89c2d633 | ||
|
|
5eddd9ff05 | ||
|
|
9fdfb2a043 | ||
|
|
2bdb806aaf | ||
|
|
a1b3f1a1f7 | ||
|
|
dc333e5a9a | ||
|
|
f0ecff9199 | ||
|
|
e3ccc655c5 | ||
|
|
04a96d4ab8 | ||
|
|
71e0fcec75 | ||
|
|
963d3cf3d9 | ||
|
|
2669b0efe8 | ||
|
|
a8b4eb6047 | ||
|
|
cc7b2f574c | ||
|
|
150de6d7c0 | ||
|
|
1da3f6420e | ||
|
|
919dfa43d0 | ||
|
|
b6805ce948 | ||
|
|
137610aa82 | ||
|
|
0cbb5b2dd1 | ||
|
|
9e59fefc8a | ||
|
|
6ead92db54 | ||
|
|
388b5b8b24 | ||
|
|
e15e22ecda | ||
|
|
8336bb0345 | ||
|
|
fc6c6286db | ||
|
|
3af4482db6 | ||
|
|
c9f8e9b824 | ||
|
|
d9c2813e50 | ||
|
|
84d6f9728e | ||
|
|
d1b730113b | ||
|
|
731be57e74 | ||
|
|
9f7fb9dad2 | ||
|
|
9f81879713 | ||
|
|
ff8527f007 | ||
|
|
2e07d2a2ca | ||
|
|
8f5a84969b | ||
|
|
1e76a4d99f | ||
|
|
840621a6b1 | ||
|
|
24485f9615 | ||
|
|
d4f0ae8ad7 | ||
|
|
a06928378a | ||
|
|
bf93ca78c8 | ||
|
|
75180877b1 | ||
|
|
fb4aaf7ecb | ||
|
|
d58355209a | ||
|
|
8488b7e18b | ||
|
|
e7aced94dd | ||
|
|
3dd93c33b3 | ||
|
|
bd613a3304 | ||
|
|
7028dcd905 | ||
|
|
9ef063cf98 | ||
|
|
bf13e1cdba | ||
|
|
79f5f53886 | ||
|
|
4c951d7e44 | ||
|
|
b034e514a7 | ||
|
|
b88a26dcae | ||
|
|
ee30ba4fc8 | ||
|
|
a02b89ba3e | ||
|
|
2ca3ab8274 | ||
|
|
cbf0210131 | ||
|
|
d8d9420616 | ||
|
|
891f792fe4 | ||
|
|
d538e47917 | ||
|
|
6c7ce98522 | ||
|
|
1e839548a4 | ||
|
|
4ea45ae375 | ||
|
|
91f89c6b7d | ||
|
|
0ac70a5a43 | ||
|
|
9ac354cb23 | ||
|
|
40e6fcd966 | ||
|
|
7e476ded1c | ||
|
|
a885c0a179 | ||
|
|
08c0b297e5 | ||
|
|
01ed04c791 | ||
|
|
0135a83b49 | ||
|
|
b9c177bb54 | ||
|
|
00ce23bc99 | ||
|
|
4853b6cc5d | ||
|
|
79dea425ea | ||
|
|
c20faed4e3 | ||
|
|
e9acd28126 | ||
|
|
b71b22f5a7 | ||
|
|
bc7d0e09b8 | ||
|
|
f682ade2c9 | ||
|
|
2b990ff433 | ||
|
|
6d21c2b074 | ||
|
|
3840720620 | ||
|
|
7dd8668b6e | ||
|
|
ac4b8b0b80 | ||
|
|
febe890ecb | ||
|
|
1be2ad4d6e | ||
|
|
848969edbc | ||
|
|
f0d93ad736 | ||
|
|
9fb17d0483 | ||
|
|
63a637a50e | ||
|
|
54487d999c | ||
|
|
6916c5a4d5 | ||
|
|
00d765b563 | ||
|
|
cb4985dc0b | ||
|
|
2b7d69e364 | ||
|
|
5075698f4a | ||
|
|
4c6fbeffb7 | ||
|
|
262dd4123d | ||
|
|
3ccc25e08b | ||
|
|
65ab1bab79 | ||
|
|
4f5f3b9f7c | ||
|
|
fe4dfb2a1c | ||
|
|
b71fe13787 | ||
|
|
340ab407da | ||
|
|
a00d96e227 | ||
|
|
b3d2cc0196 | ||
|
|
710fbcbff5 | ||
|
|
2d04b485ae | ||
|
|
ce81d90e3a | ||
|
|
18b806eb2a | ||
|
|
b898a4ca96 | ||
|
|
069a33773c | ||
|
|
b40c0db7a0 | ||
|
|
7efddf713a | ||
|
|
a9982b18ad | ||
|
|
835f41bc3a | ||
|
|
82a57051fe | ||
|
|
b54ed8115a | ||
|
|
481a2ffe2d | ||
|
|
4ca7a1622e | ||
|
|
3f90ae73c7 | ||
|
|
45e423dc09 | ||
|
|
efe60c23ce | ||
|
|
434a5e41a9 | ||
|
|
2dd6933862 | ||
|
|
cbd3ffd3a5 | ||
|
|
1efd35a796 | ||
|
|
c8f9a08bfc | ||
|
|
073e64d7f9 | ||
|
|
6c72d31edc | ||
|
|
8d68717ccc | ||
|
|
9f7dd15bf8 | ||
|
|
c561db7997 | ||
|
|
9e72c74a8d | ||
|
|
e8b360efc6 | ||
|
|
4679125bb7 | ||
|
|
37f68f1604 | ||
|
|
acd76b9d6b | ||
|
|
61b744971a | ||
|
|
155a7f9305 | ||
|
|
a677443a7c | ||
|
|
fccebf853e | ||
|
|
6ebaf6750b | ||
|
|
00c332e225 | ||
|
|
b0ba677938 | ||
|
|
1de22bbc0b | ||
|
|
beee4bd35d | ||
|
|
74f5e3b1c2 | ||
|
|
ed70775697 | ||
|
|
711f66e4f5 | ||
|
|
70a791cba9 | ||
|
|
65d62fee5e | ||
|
|
9684278160 | ||
|
|
68008d67f7 | ||
|
|
aef5bbc142 | ||
|
|
77223c9fd7 | ||
|
|
25acedd3ee | ||
|
|
8b93feb50e | ||
|
|
d353c9f4ae | ||
|
|
f3a0ef6de8 | ||
|
|
e2944f383f | ||
|
|
0103a939d2 | ||
|
|
34b82cfa2e | ||
|
|
8c7ed30bcd | ||
|
|
4872ea1bb5 | ||
|
|
d57b747991 | ||
|
|
cc1ee6f999 | ||
|
|
5d85d83ce1 | ||
|
|
0d2fa86c03 | ||
|
|
9e26b22058 | ||
|
|
d411763118 | ||
|
|
3b99a588f7 | ||
|
|
fa3239e566 | ||
|
|
b52ba30794 | ||
|
|
7cd4dc562a | ||
|
|
307d935619 | ||
|
|
3060ad8d3a | ||
|
|
9d0591c617 | ||
|
|
b7520c88c1 | ||
|
|
e447657c94 | ||
|
|
1955179da4 | ||
|
|
151b52fb37 | ||
|
|
55ed0a641a | ||
|
|
a67632a48a | ||
|
|
0b7b6ca623 | ||
|
|
d85fc4a71f | ||
|
|
9a680f0ef8 | ||
|
|
ee6bcd8577 | ||
|
|
e6ac313875 | ||
|
|
34e59e653f | ||
|
|
2eaf3e814a | ||
|
|
958d390efb | ||
|
|
7de6c27bd8 | ||
|
|
7e01f9d0ed | ||
|
|
cd9b9f48f7 | ||
|
|
d52b2f5228 | ||
|
|
1ca892fc2d | ||
|
|
891246a672 | ||
|
|
bfceff6446 | ||
|
|
0f8d3f935a | ||
|
|
e1c1b33232 | ||
|
|
1419c3231e | ||
|
|
5c06c1cd88 | ||
|
|
90ebd719a0 | ||
|
|
6c896b34aa | ||
|
|
5b73049ac4 | ||
|
|
342f3d3ad8 | ||
|
|
f89f87d92f | ||
|
|
e4ffc0bcd1 | ||
|
|
664a9e07ad | ||
|
|
593639f92e | ||
|
|
61da5eb6ef | ||
|
|
f210f154c3 | ||
|
|
15e472aede | ||
|
|
1b7766ca87 | ||
|
|
c7e2c8d3fd | ||
|
|
9441131f07 | ||
|
|
697ed7282a | ||
|
|
ac26e67436 | ||
|
|
d9cc493b71 | ||
|
|
8d4219af87 | ||
|
|
d229a628ba | ||
|
|
802749fc94 | ||
|
|
4f046f8d0e | ||
|
|
8da91b3b25 | ||
|
|
6a4195098b | ||
|
|
15b15a7fd3 | ||
|
|
038a1c52a0 | ||
|
|
d0bb5b7824 | ||
|
|
8f58754a0e | ||
|
|
91ae48c852 | ||
|
|
f0d792119c | ||
|
|
389be3c9f2 | ||
|
|
0b37cfd332 | ||
|
|
2f20159382 | ||
|
|
afe196d857 | ||
|
|
77cf80a277 | ||
|
|
11cd3276d2 | ||
|
|
d2d0015cf3 | ||
|
|
0c2366cf41 | ||
|
|
59dfd1f0ef | ||
|
|
205572773c | ||
|
|
938bd18522 | ||
|
|
3a3a28b209 | ||
|
|
9e0aaa29c4 | ||
|
|
f0d51b8baf | ||
|
|
f4c1cfde79 | ||
|
|
0f1a205766 | ||
|
|
0978293cb1 | ||
|
|
6b40ee6f87 | ||
|
|
738bd10b2e | ||
|
|
733c4801df | ||
|
|
c1b489e59d | ||
|
|
407f090e1f | ||
|
|
874972198a | ||
|
|
0050d5922c | ||
|
|
f8d6dd6891 | ||
|
|
9b3b6c97dd | ||
|
|
92d340ff9c | ||
|
|
703d7c530f | ||
|
|
d4461e3715 | ||
|
|
18c71b5fe8 | ||
|
|
3d1cbae163 | ||
|
|
2cdb56d04f | ||
|
|
df81a20f74 | ||
|
|
0cab1ccabe | ||
|
|
aec362743b | ||
|
|
9dccfe49e7 | ||
|
|
fcf4849c4b | ||
|
|
d4a8db8554 | ||
|
|
59a47ba9d5 | ||
|
|
e85bdb5cf3 | ||
|
|
4494e53e9b | ||
|
|
5382e98c0b | ||
|
|
1c095c339f | ||
|
|
0153ae79e3 | ||
|
|
30be52032a | ||
|
|
c52769ced5 | ||
|
|
f247782e40 | ||
|
|
ac87f46b3a | ||
|
|
13e2bab3dd | ||
|
|
b676c0511d | ||
|
|
3d819081f5 | ||
|
|
97d290ad5d | ||
|
|
02bd2a3ac2 | ||
|
|
44ef08d1d6 | ||
|
|
298c26cd56 | ||
|
|
768a04c833 | ||
|
|
7ef4cf9c52 | ||
|
|
8c95417359 | ||
|
|
336126cb14 | ||
|
|
5d3bb57f25 | ||
|
|
f85c200cf0 | ||
|
|
97898c584d | ||
|
|
e23819400e | ||
|
|
e63d0b7ebb | ||
|
|
6365389129 | ||
|
|
cd14cac17c | ||
|
|
7897407ec9 | ||
|
|
05394876d5 | ||
|
|
eda835c7e7 | ||
|
|
ddec9d2adf | ||
|
|
2ae3ac05b5 | ||
|
|
0fe312143a | ||
|
|
392583ba61 | ||
|
|
177e002b99 | ||
|
|
4fde28675e | ||
|
|
cd60adbcef | ||
|
|
6fe8375446 | ||
|
|
5bdb2c762c | ||
|
|
9e01dc2067 | ||
|
|
70c6ee24a4 | ||
|
|
caf690fe9e | ||
|
|
1de9565e55 | ||
|
|
eb58cac600 | ||
|
|
04491b88d6 | ||
|
|
af4c4b78db | ||
|
|
f835fde473 | ||
|
|
b8ee6689fd | ||
|
|
afca21e3bf | ||
|
|
05ee606e4f | ||
|
|
326d9b3d8f | ||
|
|
a1c133c939 | ||
|
|
90e6fa102e | ||
|
|
3adf7c55e2 | ||
|
|
6deaac8a9d | ||
|
|
2c9c487cbd | ||
|
|
171c17e255 | ||
|
|
a694ee8e45 | ||
|
|
9d647da0c1 | ||
|
|
d8cd37b06b | ||
|
|
3ac61a63ea | ||
|
|
6386841780 | ||
|
|
bd7537ad71 | ||
|
|
d22367db53 | ||
|
|
d85671f218 | ||
|
|
2e5f4fd610 | ||
|
|
a09a3fd4f0 | ||
|
|
69e5a839d5 | ||
|
|
d9c180743e | ||
|
|
90eeefca1c | ||
|
|
3073d84451 | ||
|
|
e5e40fa26c | ||
|
|
44003e258d | ||
|
|
445cb60b5a | ||
|
|
96b384754e | ||
|
|
fc58b3d4bf | ||
|
|
e407eeedf7 | ||
|
|
8f0e5f98b9 | ||
|
|
470935e758 | ||
|
|
0a29106a3b | ||
|
|
ce6e80b9da | ||
|
|
f6c8abeba2 | ||
|
|
23e37421ee | ||
|
|
d53df2fb4b | ||
|
|
fab41cfa6f | ||
|
|
84b2d45849 | ||
|
|
d1b4cb0004 | ||
|
|
6425ceedc0 | ||
|
|
2d6dee0c98 | ||
|
|
81139ced6d | ||
|
|
a2e4bccfc6 | ||
|
|
a41e8ddd06 | ||
|
|
36b5e88c85 | ||
|
|
356b854b09 | ||
|
|
9b7e5ff81c | ||
|
|
d2efda75cf | ||
|
|
461e6baf3d | ||
|
|
09341b485a | ||
|
|
0c397e4b16 | ||
|
|
4a9ddb5822 | ||
|
|
0d88ef45e2 | ||
|
|
9d24788262 | ||
|
|
26672a8054 | ||
|
|
8fddf53d4f | ||
|
|
ff96cd9702 | ||
|
|
7c9b8b4f3e | ||
|
|
181e7ecdaf | ||
|
|
d65a52e80d | ||
|
|
7a32add3d6 | ||
|
|
af36f2cce2 | ||
|
|
2d5c615291 | ||
|
|
e48de7ba8c | ||
|
|
dfba394a88 | ||
|
|
0738e6e782 | ||
|
|
bb804cdb37 | ||
|
|
1c53c52564 | ||
|
|
06133012f7 | ||
|
|
80b879e02a | ||
|
|
fe0e95ddbd | ||
|
|
dcf9297ac6 | ||
|
|
b0f04c55e0 | ||
|
|
c01a65ccbd | ||
|
|
34b25f8d1c | ||
|
|
44b3f1f100 | ||
|
|
8743d79d56 | ||
|
|
2deea89c1b | ||
|
|
ce8bea8efa | ||
|
|
81c4031d2f | ||
|
|
a993601f76 | ||
|
|
1f5494e524 | ||
|
|
de7c2c213d | ||
|
|
2b0ab60ee6 | ||
|
|
8329bee93c | ||
|
|
0b9f364448 | ||
|
|
7719f9bcc7 | ||
|
|
e25f4a965d | ||
|
|
2cf81ea592 | ||
|
|
6747a7133e | ||
|
|
a80beaf182 | ||
|
|
af132d0e98 | ||
|
|
4e2f78e37d | ||
|
|
60ed699672 | ||
|
|
a244286d65 | ||
|
|
7d9f15ab76 | ||
|
|
346595f0cf | ||
|
|
7dee18c5c7 | ||
|
|
546ebee48e | ||
|
|
9a6312d388 | ||
|
|
7b7caafcb6 | ||
|
|
1f8f5d5f0b | ||
|
|
e0cd30baf9 | ||
|
|
bac1271a96 | ||
|
|
1666681872 | ||
|
|
0f87730f2e | ||
|
|
fc69bf7180 | ||
|
|
bfe0ee4fc2 | ||
|
|
431258ca0c | ||
|
|
9941522ce6 | ||
|
|
25256f5e0a | ||
|
|
b8eb5cf283 | ||
|
|
e9560857d9 | ||
|
|
9b1a83f44f | ||
|
|
d54b3eeb72 | ||
|
|
ed547206e6 | ||
|
|
27b6fcde97 | ||
|
|
d5b67a26d5 | ||
|
|
2af6e3e213 | ||
|
|
dbc8e268bc | ||
|
|
5334763d76 | ||
|
|
924a080d94 | ||
|
|
37112f90b7 | ||
|
|
e04f2eb420 | ||
|
|
067730cef0 | ||
|
|
d9c4cf9e5f | ||
|
|
9659460836 | ||
|
|
94c789b867 | ||
|
|
99b7dc80d3 | ||
|
|
d32f42feb7 | ||
|
|
913a3bec5e | ||
|
|
2770c13a56 | ||
|
|
0a4d94f133 | ||
|
|
3384b6abe9 | ||
|
|
7d79beaf7c | ||
|
|
c478608b1b | ||
|
|
d4d1729b0f | ||
|
|
3e793c55db | ||
|
|
8924afdadd | ||
|
|
8cb439c1b9 | ||
|
|
231ff2bbbf | ||
|
|
1b8b809122 | ||
|
|
4222adae59 | ||
|
|
f39e85744c | ||
|
|
2858a770df | ||
|
|
f567841ab2 | ||
|
|
9b7a74e1a4 | ||
|
|
b2783d7945 | ||
|
|
7451a4723a | ||
|
|
617279573b | ||
|
|
89d39ebf54 | ||
|
|
4bcc16b4f7 | ||
|
|
3bba26399a | ||
|
|
4514f0a25f | ||
|
|
8fa9a869a0 | ||
|
|
f3776c8fb5 | ||
|
|
ada8fa6d91 | ||
|
|
957ddfe70e | ||
|
|
77cc5ff890 | ||
|
|
5d8d05a291 | ||
|
|
8a17ab5025 | ||
|
|
93b1ab7b12 | ||
|
|
751793b313 | ||
|
|
67d24fe4a4 | ||
|
|
7f42a79d68 | ||
|
|
bc5a0540e8 | ||
|
|
43080cbf4c | ||
|
|
b70666decb | ||
|
|
1d52311bd7 | ||
|
|
672cda65ae | ||
|
|
8a4dc9d926 | ||
|
|
b88f6f3cd6 | ||
|
|
11efba69c8 | ||
|
|
e96371b7c6 | ||
|
|
04639e0e9b | ||
|
|
2fbad53fad | ||
|
|
4c5d48a51b | ||
|
|
9783c5e434 | ||
|
|
7efff73127 | ||
|
|
c0c1797ff4 | ||
|
|
49eb8907fd | ||
|
|
5f0c8a0be6 | ||
|
|
4b092a6688 | ||
|
|
87156fefcb | ||
|
|
017e2e10d6 | ||
|
|
20d03053e7 | ||
|
|
c2890e7a43 | ||
|
|
225f93f0f0 | ||
|
|
304d691e1c | ||
|
|
2de21a64d9 | ||
|
|
1f81be4c94 | ||
|
|
2f012d9b0b | ||
|
|
f5a5117a71 | ||
|
|
f44adad3ec | ||
|
|
f15c84c584 | ||
|
|
25552f7994 | ||
|
|
e60c4a6ee9 | ||
|
|
a1de9c2138 | ||
|
|
182f631936 | ||
|
|
a16977d49b | ||
|
|
c2698b4edb | ||
|
|
0e9498009b | ||
|
|
a37992c381 | ||
|
|
088d97784f | ||
|
|
e2de49ad79 | ||
|
|
0c66798762 | ||
|
|
2cb8e4044c | ||
|
|
12bd0b5d91 | ||
|
|
5d2c0904f6 | ||
|
|
f0ea373b47 | ||
|
|
6f1f48c18f | ||
|
|
91a0406d44 | ||
|
|
e58ae853cf | ||
|
|
7d679359d3 | ||
|
|
0b645e66e5 | ||
|
|
07c5e8fd03 | ||
|
|
a5341c61df | ||
|
|
77f492475f | ||
|
|
566595e7a7 | ||
|
|
9e873fe809 | ||
|
|
c55e772fba | ||
|
|
8db752f425 | ||
|
|
19cba53ba5 | ||
|
|
2e6b99fc21 | ||
|
|
a36f4fb27b | ||
|
|
4de9b00974 | ||
|
|
8d60195cd8 | ||
|
|
f224a55978 | ||
|
|
9ccf4d48e9 | ||
|
|
a03e98551d | ||
|
|
02b3173084 | ||
|
|
fcd44bcced | ||
|
|
bb2e68374b | ||
|
|
886bbd8e30 | ||
|
|
8d7e5ca2e3 | ||
|
|
03070989ac | ||
|
|
0c64ca0a4f | ||
|
|
8f3a1ce92c | ||
|
|
9a8b979dd1 | ||
|
|
3120b813b9 | ||
|
|
667fc66397 | ||
|
|
2ad686433a | ||
|
|
d03534f9a6 | ||
|
|
e270dbc951 | ||
|
|
3c76f52e69 | ||
|
|
ddef66bba2 | ||
|
|
231a6b3ef2 | ||
|
|
7c4b40d626 | ||
|
|
05ad1c4f9c | ||
|
|
35df0c14ae | ||
|
|
a9e0b1288a | ||
|
|
848d7d4894 | ||
|
|
0d96759e97 | ||
|
|
53d83b5aac | ||
|
|
8cbbd1153c | ||
|
|
fd1be636ab | ||
|
|
9ca2a80abf | ||
|
|
f41aecd0eb | ||
|
|
11d107e2c4 | ||
|
|
446213b7df | ||
|
|
a27b015792 | ||
|
|
ff03509861 | ||
|
|
052974639a | ||
|
|
5a1b36129d | ||
|
|
f21e987ab0 | ||
|
|
177ca04e02 | ||
|
|
5cd0129b6c | ||
|
|
3f1313ae3b | ||
|
|
12ee6ab936 | ||
|
|
a05c31b4de | ||
|
|
72d205f3e9 | ||
|
|
dcde2b43f1 | ||
|
|
9ab36c65d7 | ||
|
|
323455c924 | ||
|
|
0ab5d1f290 | ||
|
|
d1986c4f6f | ||
|
|
1eff8309ea | ||
|
|
95e387f40f | ||
|
|
264b4649ea | ||
|
|
af5e721a18 | ||
|
|
f3a0d7c069 | ||
|
|
9fea95e3dd | ||
|
|
bc14540ccc | ||
|
|
7bfe22fe10 | ||
|
|
09d4dfb6ae | ||
|
|
3f4f0cb6c5 | ||
|
|
796cf2ce53 | ||
|
|
59ebfd3d6c | ||
|
|
0787595f98 | ||
|
|
6e58a80bc1 | ||
|
|
bd621067b5 | ||
|
|
01ae389264 | ||
|
|
84700cfc2f | ||
|
|
b68f609a44 | ||
|
|
703e198780 | ||
|
|
4f75ef8709 | ||
|
|
024c5fc50c | ||
|
|
f87b2248b2 | ||
|
|
bf65d419b6 | ||
|
|
267df53691 | ||
|
|
6c38a9048d | ||
|
|
b30b98c447 | ||
|
|
3c58913df1 | ||
|
|
7ec7098849 | ||
|
|
040ea45b88 | ||
|
|
fba2210e24 | ||
|
|
583750fc35 | ||
|
|
bb6f7c4913 | ||
|
|
873a2f98dc | ||
|
|
c9c6ab4d58 | ||
|
|
b2710b1f1c | ||
|
|
3348b628ec | ||
|
|
7f726f48eb | ||
|
|
7c9b52d4a6 | ||
|
|
59732696c1 | ||
|
|
dd55b7f4c3 | ||
|
|
7577374e4b | ||
|
|
f861fa282a | ||
|
|
34f5005843 | ||
|
|
c07a56dee3 | ||
|
|
1e98599dc1 | ||
|
|
c94eaba7a5 | ||
|
|
f61be573e6 | ||
|
|
e44d367ee7 | ||
|
|
6dd9ac452c | ||
|
|
da746c4134 | ||
|
|
c691c495fa | ||
|
|
349584eb16 | ||
|
|
8e8e428300 | ||
|
|
ae8fb326a9 | ||
|
|
599579ff59 | ||
|
|
022684b8dd | ||
|
|
3410480b67 | ||
|
|
3087f133a5 | ||
|
|
0c69206bd0 | ||
|
|
eb2bf34227 | ||
|
|
25f0e26fe7 | ||
|
|
01a115c3d0 | ||
|
|
19885c86d9 | ||
|
|
af5da7fe44 | ||
|
|
eb529ac941 | ||
|
|
c216e5a239 | ||
|
|
9022e16d0a | ||
|
|
67358cad18 | ||
|
|
52b9c5f408 | ||
|
|
c9c32a2d96 | ||
|
|
b2a780ebb5 | ||
|
|
19e188b7f7 | ||
|
|
d720ce3dc4 | ||
|
|
adddf74c48 | ||
|
|
2a5e54f8cb | ||
|
|
ab9b9ec818 | ||
|
|
a8faad7b17 | ||
|
|
955a4dccb7 | ||
|
|
174f6f42b9 | ||
|
|
c1279b3089 | ||
|
|
14661ed00b | ||
|
|
b9cbe4e955 | ||
|
|
53c4a64aa1 | ||
|
|
b85a5a4673 | ||
|
|
93a4f6fe5a | ||
|
|
a7bb22fa81 | ||
|
|
9c463c1705 | ||
|
|
95935fc7c1 | ||
|
|
ed66b5e603 | ||
|
|
4805c8abbf | ||
|
|
77d6b7463b | ||
|
|
651ac9d973 | ||
|
|
73b97a8c89 | ||
|
|
996b762d5b | ||
|
|
82dd6c7e79 | ||
|
|
5d5bc6dd24 | ||
|
|
05362e98a7 | ||
|
|
d6f5dd9540 | ||
|
|
f70a509213 | ||
|
|
b094f8e3c9 | ||
|
|
e06cbc719c | ||
|
|
2027f26053 | ||
|
|
fc0c7ac4df | ||
|
|
999c95694b | ||
|
|
c856a65b13 | ||
|
|
dc6d5bad8c | ||
|
|
3da53cc323 | ||
|
|
e4eaaeb2be | ||
|
|
76e4a05ce0 | ||
|
|
e01c6d4c69 | ||
|
|
b86250889d | ||
|
|
98e1f58819 | ||
|
|
eb3b7c6fe9 | ||
|
|
ba7c366b97 | ||
|
|
874252dbe6 | ||
|
|
05e79f0e90 | ||
|
|
d119336f9c | ||
|
|
f6674c149d | ||
|
|
3b3947c1c1 | ||
|
|
523fab8036 | ||
|
|
04b873aa37 | ||
|
|
3e0596535f | ||
|
|
fcaf430775 | ||
|
|
33449562cb | ||
|
|
d17987d64b | ||
|
|
30e8ade45a | ||
|
|
ef3cbf5993 | ||
|
|
c943c8fa08 | ||
|
|
7b2db0faf8 | ||
|
|
5a6f926085 | ||
|
|
1016adf0ca | ||
|
|
7fb5f713dd | ||
|
|
c99caa035f | ||
|
|
2fc24ef753 | ||
|
|
3538d6a7c8 | ||
|
|
c6d19a3e40 | ||
|
|
a26e8f4360 | ||
|
|
e5aac45f1c | ||
|
|
154eaad8f1 | ||
|
|
fcfba0c6b5 | ||
|
|
7137a5c834 | ||
|
|
c1dfcab0a6 | ||
|
|
93c2d88dbc | ||
|
|
edd9a27720 | ||
|
|
eda9ae4fd9 | ||
|
|
931902fd8b | ||
|
|
fa5703d6e0 | ||
|
|
f5e2432eec | ||
|
|
e95a9448fe | ||
|
|
8dd40a0c9f | ||
|
|
79abdaed28 | ||
|
|
597d4dbbd1 | ||
|
|
94859f8f54 | ||
|
|
f779e0487d | ||
|
|
0df40c4fea | ||
|
|
aa902601fb | ||
|
|
60e91a5ae7 | ||
|
|
17f6897d47 | ||
|
|
72383a0e08 | ||
|
|
afd88967ab | ||
|
|
bece167fea | ||
|
|
82d3563e62 | ||
|
|
a1579d71c2 | ||
|
|
8d6170d645 | ||
|
|
9e345f2b4b | ||
|
|
f00ffac57a | ||
|
|
3960a8356e | ||
|
|
7370986186 | ||
|
|
39b32b03d2 | ||
|
|
fba2d99ae2 | ||
|
|
e586c55478 | ||
|
|
16d1a82aff | ||
|
|
0c47986d56 | ||
|
|
44dabbf05f | ||
|
|
5c43e8c197 | ||
|
|
1b99bcd4fa | ||
|
|
6e90f4677a | ||
|
|
3253e6d45c | ||
|
|
0e49c38853 | ||
|
|
c906384e53 | ||
|
|
1e3fbac132 | ||
|
|
e54b0e8e63 | ||
|
|
eeade17262 | ||
|
|
79e51aea4e | ||
|
|
a4e8b06010 | ||
|
|
558289e904 | ||
|
|
14fba98000 | ||
|
|
5538de0258 | ||
|
|
0fbababecd | ||
|
|
f518f438ab | ||
|
|
f420fb5cfd | ||
|
|
6bee451636 | ||
|
|
109805894c | ||
|
|
dbce93b40d | ||
|
|
211585016d | ||
|
|
6b7ba78395 | ||
|
|
f48d7c891b | ||
|
|
6206d7dded | ||
|
|
6371933252 | ||
|
|
94754cf29c | ||
|
|
6c3415b6f3 | ||
|
|
008a98fcfa | ||
|
|
2e5e04332b | ||
|
|
83f9576de6 | ||
|
|
16ba3bfd84 | ||
|
|
24764a67bf | ||
|
|
2f638aabe3 | ||
|
|
db134ec18a | ||
|
|
6d6a0504d8 | ||
|
|
9bcd9921d5 | ||
|
|
47e16fd386 | ||
|
|
2d29536fbc | ||
|
|
861abc2edd | ||
|
|
3d9ece48fa | ||
|
|
7db57dcd0b | ||
|
|
977e8af7e0 | ||
|
|
2a8683a2d5 | ||
|
|
fbce496de4 | ||
|
|
364a251041 | ||
|
|
cabc3a96d8 | ||
|
|
229ade32c3 | ||
|
|
c1ed9fdd92 | ||
|
|
7dc0ea9d19 | ||
|
|
bce0598ebe | ||
|
|
fa2c7605e2 | ||
|
|
6a372bacdc | ||
|
|
d9e4245bee | ||
|
|
9a34bcb023 | ||
|
|
93d07d9b69 | ||
|
|
c10316cbc6 | ||
|
|
3155e28fe3 | ||
|
|
3b2c34d6fe | ||
|
|
edce679f88 | ||
|
|
dce93b3679 | ||
|
|
1288863264 | ||
|
|
63f7a734ea | ||
|
|
ba86df566d | ||
|
|
308f7cef39 | ||
|
|
53f37fa68a | ||
|
|
3479584f71 | ||
|
|
312b387581 | ||
|
|
ec51c719c3 | ||
|
|
10c09a92fa | ||
|
|
7cebb701ad | ||
|
|
972355a45a | ||
|
|
5716a7bef5 | ||
|
|
9486a04572 | ||
|
|
00fe255223 | ||
|
|
d89e7088b7 | ||
|
|
75ff7631bd | ||
|
|
7da6b44aab | ||
|
|
727dcfcf28 | ||
|
|
0e3b1efb53 | ||
|
|
bb05d683ed | ||
|
|
48c60b84d3 | ||
|
|
7a83824e0d | ||
|
|
47726d614f | ||
|
|
4ae5a3fd02 | ||
|
|
12a624555b | ||
|
|
4a9484a544 | ||
|
|
714d5122fb | ||
|
|
33c8bbe9b1 | ||
|
|
5995bc1c5b | ||
|
|
dd3dab4294 | ||
|
|
6c6ca70bd0 | ||
|
|
2512a22073 | ||
|
|
92f3d7e22e | ||
|
|
91d965f61c | ||
|
|
772288dbaf | ||
|
|
37d4084013 | ||
|
|
0e53f8b50d | ||
|
|
c99861fc30 | ||
|
|
4cb361fd51 | ||
|
|
45fec00545 | ||
|
|
ad5bf0e5e6 | ||
|
|
59439d81b5 | ||
|
|
7af6628d38 | ||
|
|
bc2b672b9b | ||
|
|
38e98915b7 | ||
|
|
0a25166bbd | ||
|
|
c124b0abc1 | ||
|
|
230b501e23 | ||
|
|
05bc345523 | ||
|
|
f7d1a41717 | ||
|
|
4514764aba | ||
|
|
90fd84722b | ||
|
|
ca08478389 | ||
|
|
8a21a4cad5 | ||
|
|
5374e2f621 | ||
|
|
0f0e9846c2 | ||
|
|
737beefad2 | ||
|
|
c506bf566a | ||
|
|
10ba5c7623 | ||
|
|
9fab4c1aaa | ||
|
|
2daf3d8180 | ||
|
|
f9412bf098 | ||
|
|
d8eac4825b | ||
|
|
225d6029fc | ||
|
|
e55d31656b | ||
|
|
7ffeb2ff0b | ||
|
|
3e9869c190 | ||
|
|
4389dba285 | ||
|
|
77d418fa99 | ||
|
|
170ea5a524 | ||
|
|
6960d5a4fa | ||
|
|
6409aa91ff |
No files matched your search
@@ -0,0 +1,79 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
node: circleci/node@5.0.3
|
||||
codecov: codecov/codecov@3.3.0
|
||||
|
||||
executors:
|
||||
base:
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
node:
|
||||
docker:
|
||||
- image: 'cimg/node:18.19.0'
|
||||
go:
|
||||
docker:
|
||||
- image: cimg/go:1.21.3
|
||||
|
||||
parameters:
|
||||
trigger-app:
|
||||
type: boolean
|
||||
default: false
|
||||
run-them-all:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
## Backend
|
||||
test_app:
|
||||
executor: go
|
||||
working_directory: ~/go/src/github.com/ArtalkJS/Artalk
|
||||
environment:
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Print the Go version"
|
||||
command: >
|
||||
go version
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-mod-1.21.3-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: go mod download
|
||||
- save_cache:
|
||||
key: go-mod-1.21.3-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- "~/go/pkg/mod"
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports
|
||||
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -coverprofile=/tmp/test-reports/coverage.out ./...
|
||||
- store_test_results:
|
||||
path: /tmp/test-reports
|
||||
- codecov/upload:
|
||||
flags: go
|
||||
file: /tmp/test-reports/coverage.out
|
||||
|
||||
all_projects:
|
||||
executor: base
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
echo "all"
|
||||
|
||||
workflows:
|
||||
build-app:
|
||||
when:
|
||||
or:
|
||||
- << pipeline.parameters.trigger-app >>
|
||||
- << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
- test_app
|
||||
|
||||
build-shared-other:
|
||||
when: << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
- all_projects
|
||||
@@ -0,0 +1,25 @@
|
||||
# https://circleci.com/docs/2.0/configuration-reference
|
||||
version: 2.1
|
||||
|
||||
# this allows you to use CircleCI's dynamic configuration
|
||||
# https://circleci.com/docs/2.0/dynamic-config/
|
||||
setup: true
|
||||
|
||||
# the path-filtering orb is required to continue a pipeline based on
|
||||
# https://circleci.com/developer/orbs/orb/circleci/path-filtering
|
||||
orbs:
|
||||
path-filtering: circleci/path-filtering@1.0.0
|
||||
|
||||
workflows:
|
||||
setup:
|
||||
jobs:
|
||||
- path-filtering/filter:
|
||||
mapping: |
|
||||
(cmd|internal|server|pkg|test|conf)/.* trigger-app true
|
||||
main.go|go.mod|go.sum trigger-app true
|
||||
base-revision: master # git branch name
|
||||
config-path: .circleci/conditional_config.yml
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages
|
||||
@@ -13,8 +13,19 @@ indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.sh]
|
||||
indent_size = 4
|
||||
|
||||
[Dockerfile]
|
||||
indent_size = 4
|
||||
@@ -1,11 +0,0 @@
|
||||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
|
||||
vite*.config*
|
||||
|
||||
# 不检测 js
|
||||
**/*.js
|
||||
@@ -1,35 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: [
|
||||
'airbnb-base',
|
||||
'airbnb-typescript/base',
|
||||
'prettier',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
],
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'no-plusplus': 0,
|
||||
'no-param-reassign': 0,
|
||||
'no-console': 0,
|
||||
'no-constructor-return': 0,
|
||||
'class-methods-use-this': 0,
|
||||
'spaced-comment': 0,
|
||||
'no-lonely-if': 0,
|
||||
'prefer-destructuring': 0,
|
||||
'import/no-cycle': 0,
|
||||
'@typescript-eslint/lines-between-class-members': 0,
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 0,
|
||||
},
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: artalk # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
- https://afdian.net/a/artalk
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
{{ range .Versions }}
|
||||
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
|
||||
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
|
||||
{{ range .Commits -}}
|
||||
* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
{{- if .NoteGroups -}}
|
||||
{{ range .NoteGroups -}}
|
||||
### {{ .Title }}
|
||||
|
||||
{{ range .Notes }}
|
||||
{{ .Body }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
style: github
|
||||
template: CHANGELOG.tpl.md
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://github.com/ArtalkJS/Artalk
|
||||
options:
|
||||
tag_filter_pattern: '^v'
|
||||
sort: "date"
|
||||
commits:
|
||||
filters:
|
||||
Type:
|
||||
- feat
|
||||
- fix
|
||||
- perf
|
||||
- refactor
|
||||
- docs
|
||||
commit_groups:
|
||||
sort_by: Custom
|
||||
title_order:
|
||||
- feat
|
||||
- fix
|
||||
- perf
|
||||
- refactor
|
||||
- docs
|
||||
title_maps:
|
||||
feat: Features
|
||||
fix: Bug Fixes
|
||||
perf: Performance Improvements
|
||||
refactor: Code Refactoring
|
||||
docs: Documentation
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s\\/]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
- Subject
|
||||
notes:
|
||||
keywords:
|
||||
- BREAKING CHANGE
|
||||
@@ -0,0 +1,67 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
commit-message:
|
||||
prefix: "chore(deps/go): "
|
||||
groups:
|
||||
deps:
|
||||
patterns:
|
||||
- '*'
|
||||
open-pull-requests-limit: 15
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /ui
|
||||
schedule:
|
||||
interval: monthly
|
||||
commit-message:
|
||||
prefix: "chore(deps/ui): "
|
||||
groups:
|
||||
prod-deps:
|
||||
dependency-type: production
|
||||
dev-deps:
|
||||
dependency-type: development
|
||||
open-pull-requests-limit: 15
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /docs
|
||||
schedule:
|
||||
interval: monthly
|
||||
commit-message:
|
||||
prefix: "chore(deps/docs): "
|
||||
groups:
|
||||
deps:
|
||||
patterns:
|
||||
- '*'
|
||||
open-pull-requests-limit: 15
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
commit-message:
|
||||
prefix: "chore(deps/docker): "
|
||||
groups:
|
||||
deps:
|
||||
patterns:
|
||||
- '*'
|
||||
open-pull-requests-limit: 15
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /.github/workflows
|
||||
schedule:
|
||||
interval: monthly
|
||||
commit-message:
|
||||
prefix: "chore(deps/ci): "
|
||||
groups:
|
||||
deps:
|
||||
patterns:
|
||||
- '*'
|
||||
open-pull-requests-limit: 15
|
||||
@@ -0,0 +1,84 @@
|
||||
name: Build App
|
||||
run-name: Build Go App and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.21.3'
|
||||
PKG_NAME: 'github.com/ArtalkJS/Artalk'
|
||||
DOCKER_IMG: ghcr.io/goreleaser/goreleaser-cross
|
||||
CACHE_DIR: /tmp/cache/docker-image
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout latest tag
|
||||
run: |
|
||||
# fetch tags
|
||||
git fetch --prune --unshallow --tags -f
|
||||
|
||||
# checkout latest version
|
||||
VERSION=$(git describe --tags --abbrev=0)
|
||||
git checkout ${VERSION}
|
||||
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
# disable cache because the err `no space left on device`
|
||||
# - name: Docker Image Cache
|
||||
# id: docker-cache
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ env.CACHE_DIR }}
|
||||
# key: docker-image-go-cross-${{ env.GO_VERSION }}
|
||||
|
||||
- name: Pull Docker Image
|
||||
# if: steps.docker-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
docker pull "${DOCKER_IMG}:v${GO_VERSION}"
|
||||
|
||||
# mkdir -p "${CACHE_DIR}"
|
||||
# docker save -o "${CACHE_DIR}/go-cross.tar" "${DOCKER_IMG}:v${GO_VERSION}"
|
||||
|
||||
# - name: Restore Docker Image from Cache
|
||||
# if: steps.docker-cache.outputs.cache-hit == 'true'
|
||||
# run: docker load -i ${CACHE_DIR}/go-cross.tar
|
||||
|
||||
- name: Setup git-chglog
|
||||
run: |
|
||||
curl -sL $(curl -s https://api.github.com/repos/git-chglog/git-chglog/releases/latest \
|
||||
| grep -oP '"https://.+linux_amd64.tar.gz"' | tr -d \") | tar -C /usr/local/bin -xz git-chglog
|
||||
git-chglog --version
|
||||
|
||||
- name: Pre Build
|
||||
run: |-
|
||||
mkdir -p local
|
||||
|
||||
# github token
|
||||
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" > local/.release-env
|
||||
|
||||
# changelog
|
||||
git-chglog --config .github/chglog/config.yml ${VERSION} > local/release-notes.md
|
||||
|
||||
# copy config file
|
||||
cp conf/artalk.example.yml artalk.yml
|
||||
|
||||
- name: Build and Release
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
--privileged \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v $(pwd)/sysroot:/sysroot \
|
||||
-v $(pwd):/go/src/${PKG_NAME} \
|
||||
-w /go/src/${PKG_NAME} \
|
||||
-e CGO_ENABLED=1 \
|
||||
--env-file local/.release-env \
|
||||
ghcr.io/goreleaser/goreleaser-cross:v${GO_VERSION} \
|
||||
release --release-notes local/release-notes.md
|
||||
@@ -0,0 +1,110 @@
|
||||
name: Build Docker
|
||||
run-name: Build Docker Image and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
|
||||
env:
|
||||
DOCKER_IMG: artalk/artalk-go
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout latest tag
|
||||
run: |
|
||||
# fetch tags
|
||||
git fetch --prune --unshallow --tags -f
|
||||
|
||||
# checkout latest version
|
||||
VERSION=$(git describe --tags --abbrev=0)
|
||||
git checkout ${VERSION}
|
||||
|
||||
VERSION_SHORT="$(cut -d '.' -f 1,2 <<< ${VERSION#v})" # (i.e "v1.2.3" -> "1.2")
|
||||
COMMIT_HASH="$(git rev-parse --short HEAD)"
|
||||
COMMIT_HASH_FULL="$(git rev-parse HEAD)"
|
||||
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
echo "VERSION_SHORT=${VERSION_SHORT}" >> $GITHUB_ENV
|
||||
echo "COMMIT_HASH=${COMMIT_HASH}" >> $GITHUB_ENV
|
||||
echo "COMMIT_HASH_FULL=${COMMIT_HASH_FULL}" >> $GITHUB_ENV
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Setup QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: 'amd64,arm64'
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Gen docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
context: git
|
||||
images: |
|
||||
${{ env.DOCKER_IMG }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and Push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
platforms: 'linux/amd64,linux/arm64'
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
|
||||
- name: Comment build info in commit
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
STDOUT: "🐳 Published new docker image: [${{ env.DOCKER_IMG }}](https://hub.docker.com/r/${{ env.DOCKER_IMG }}) (${{ env.VERSION }} / sha-${{ env.COMMIT_HASH }})"
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.repos.createCommitComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
commit_sha: process.env.COMMIT_HASH_FULL, // context.sha
|
||||
body: process.env.STDOUT
|
||||
})
|
||||
|
||||
- name: Print image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Build Frontend
|
||||
run-name: Build Frontend and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./ui # set for building frontend
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout latest tag
|
||||
run: |
|
||||
git fetch --prune --unshallow --tags -f
|
||||
git checkout $(git describe --tags --abbrev=0)
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: './ui/pnpm-lock.yaml'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Publish
|
||||
run: pnpm publish -F artalk --no-git-checks
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
# GitHub Packages
|
||||
#
|
||||
# Setup .npmrc file to publish to GitHub Packages
|
||||
# - uses: actions/setup-node@v2
|
||||
# with:
|
||||
# registry-url: 'https://npm.pkg.github.com'
|
||||
# # Publish to GitHub Packages
|
||||
# - run: npm publish
|
||||
# env:
|
||||
# NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,74 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '40 17 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Docs CN Mirror Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
if: github.repository == 'ArtalkJS/Artalk'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
# set for building docs
|
||||
working-directory: ./docs
|
||||
|
||||
steps:
|
||||
# - name: Wait for other docs build to succeed
|
||||
# uses: lewagon/wait-on-check-action@v1.2.0
|
||||
# with:
|
||||
# running-workflow-name: 'Docs CN Mirror Deploy'
|
||||
# check-name: 'Build Docs'
|
||||
# ref: ${{ github.ref }}
|
||||
# repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# allowed-conclusions: success,skipped,cancelled
|
||||
# wait-interval: 10
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build Docs
|
||||
run: pnpm docs:build
|
||||
|
||||
- name: Deploy
|
||||
uses: TencentCloud/cos-action@v1
|
||||
with:
|
||||
secret_id: ${{ secrets.TENCENT_CLOUD_SECRET_ID }}
|
||||
secret_key: ${{ secrets.TENCENT_CLOUD_SECRET_KEY }}
|
||||
cos_bucket: ${{ secrets.DOCS_CN_COS_BUCKET }}
|
||||
cos_region: ${{ secrets.DOCS_CN_COS_REGION }}
|
||||
local_path: .vitepress/dist/
|
||||
remote_path: /
|
||||
clean: true
|
||||
@@ -1,54 +0,0 @@
|
||||
# https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
||||
name: NPM Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Setup .npmrc file to publish to npm
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
# Setup .npmrc file to publish to GitHub Packages
|
||||
# - uses: actions/setup-node@v2
|
||||
# with:
|
||||
# registry-url: 'https://npm.pkg.github.com'
|
||||
# # Publish to GitHub Packages
|
||||
# - run: npm publish
|
||||
# env:
|
||||
# NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Docs Update
|
||||
docs-update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
|
||||
- name: Checkout ArtalkDocs
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ArtalkJS/Docs
|
||||
token: ${{ secrets.PAT }}
|
||||
|
||||
- name: Update CDN links
|
||||
run: |
|
||||
sed -i 's#artalk@.[^/]*#artalk@${{ steps.get_version.outputs.VERSION }}#g' docs/code/ArtalkCDN.json
|
||||
sed -i 's#artalk@.[^/]*#artalk@${{ steps.get_version.outputs.VERSION }}#g' docs/code/quick-start/cdn.html
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: Update Artalk CDN link to v${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- name: Install
|
||||
run: yarn install
|
||||
- name: Build
|
||||
run: yarn predeploy
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: gh-pages
|
||||
publish_dir: ./deploy
|
||||
user_name: 'github-actions[bot]'
|
||||
user_email: 'github-actions[bot]@users.noreply.github.com'
|
||||
@@ -0,0 +1,93 @@
|
||||
name: 📦 Release New Version
|
||||
run-name: 📦 Release ${{ inputs.custom || inputs.semver }} by @${{ github.actor }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
semver:
|
||||
type: choice
|
||||
description: Which version you want to increment?
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
required: true
|
||||
# custom:
|
||||
# description: 'Custom tag name (i.e v1.0.0-rc.1)'
|
||||
# required: false
|
||||
# default: ''
|
||||
|
||||
env:
|
||||
REPO_DISPATCH_EVENT_TYPE: 'artalk-release'
|
||||
UI_NPM_PKG_FILE: 'ui/packages/artalk/package.json'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Handle Version Number
|
||||
run: |
|
||||
if [ ! -z "${{ inputs.custom }}" ]; then
|
||||
next_version="${{ inputs.custom }}"
|
||||
else
|
||||
npm install -g semver
|
||||
prev_version="$(git describe --tags --abbrev=0)"
|
||||
echo "prev_version: ${prev_version}"
|
||||
next_version="v$(semver --increment ${{ inputs.semver }} ${prev_version})"
|
||||
fi
|
||||
|
||||
if [[ ! "${next_version}" =~ ^v.* ]]; then
|
||||
echo "[err] custom tag name must be v*."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "next_version: ${next_version}"
|
||||
echo "VERSION=${next_version}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup git-chglog
|
||||
run: |
|
||||
curl -sL $(curl -s https://api.github.com/repos/git-chglog/git-chglog/releases/latest \
|
||||
| grep -oP '"https://.+linux_amd64.tar.gz"' | tr -d \") | tar -C /usr/local/bin -xz git-chglog
|
||||
git-chglog --version
|
||||
|
||||
- name: Version Changelog and Tagging
|
||||
run: |
|
||||
if [ ! $(git tag -l "${VERSION}") ]; then
|
||||
# prepend changelog
|
||||
changelog=$(git-chglog --config .github/chglog/config.yml --next-tag "${VERSION}" "${VERSION}")
|
||||
echo -e "${changelog}\n" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md
|
||||
|
||||
# modify the version of `package.json`
|
||||
perl -i -pe 's/"version": ".*?"/"version": "'${VERSION#v}'"/g' "${UI_NPM_PKG_FILE}"
|
||||
|
||||
# modify the version in docs
|
||||
perl -pi -e 's#"latest"(\W+)?:(\W+)?".*?"#"latest": "'${VERSION#v}'"#g' docs/code/ArtalkVersion.json
|
||||
|
||||
# git commit and push
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
git add .
|
||||
git commit -m "chore: release ${VERSION}"
|
||||
git tag "${VERSION}"
|
||||
|
||||
git push origin master "${VERSION}"
|
||||
else
|
||||
echo "skipped because git tag already exists."
|
||||
fi
|
||||
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
event-type: ${{ env.REPO_DISPATCH_EVENT_TYPE }}
|
||||
@@ -0,0 +1,20 @@
|
||||
name: Repo Dispatch
|
||||
run-name: Dispatch event `${{ inputs.event_type }}` manually by @${{ github.actor }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
event_type:
|
||||
type: string
|
||||
description: Event Type
|
||||
required: true
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
event-type: ${{ inputs.event_type }}
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Test Frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ui/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ui/**'
|
||||
|
||||
jobs:
|
||||
test_ui:
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./ui
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
cache-dependency-path: './ui/pnpm-lock.yaml'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm --dir ./ui install --frozen-lockfile
|
||||
|
||||
- name: Package Build Test
|
||||
run: pnpm build:all
|
||||
|
||||
# TODO: Add unit test
|
||||
# - name: Unit test
|
||||
# run: pnpm test
|
||||
+62
-10
@@ -1,18 +1,70 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Editor directories and files
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.heartbeat
|
||||
.idea
|
||||
.vscode
|
||||
*~
|
||||
.goutputstream*
|
||||
.c9revisions
|
||||
.settings
|
||||
.swp
|
||||
.tmp
|
||||
.env
|
||||
.vscode/*.log
|
||||
.release-env
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.pnpm-store
|
||||
/sysroot
|
||||
.history
|
||||
|
||||
dist
|
||||
deploy
|
||||
local
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
__debug*
|
||||
|
||||
# frontend
|
||||
node_modules
|
||||
/ui/**/dist
|
||||
|
||||
# backend
|
||||
/artalk.yml
|
||||
/config.yml
|
||||
|
||||
/bin
|
||||
/data
|
||||
/local
|
||||
|
||||
# always compile latest version
|
||||
/public/sidebar
|
||||
/public/dist
|
||||
|
||||
playwright-report
|
||||
test-results
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
# Goreleaser Config
|
||||
#
|
||||
# https://goreleaser.com/quick-start/
|
||||
# https://goreleaser.com/customization/
|
||||
# https://goreleaser.com/customization/templates/
|
||||
# https://goreleaser.com/customization/fury/
|
||||
# https://goreleaser.com/customization/announce/telegram/
|
||||
|
||||
project_name: artalk
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- CGO_ENABLED=1
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# install dependencies
|
||||
- make install
|
||||
- make build-frontend
|
||||
|
||||
# build multi-platform
|
||||
builds:
|
||||
## ----------------------
|
||||
## Linux
|
||||
## ----------------------
|
||||
|
||||
# Linux (amd_64)
|
||||
- id: linux-amd64
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CC=x86_64-linux-gnu-gcc
|
||||
- CXX=x86_64-linux-gnu-g++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: &common_ldflags |
|
||||
-X github.com/ArtalkJS/Artalk/internal/config.Version={{.Version}}
|
||||
-X github.com/ArtalkJS/Artalk/internal/config.CommitHash={{ .ShortCommit }}
|
||||
-s -w
|
||||
|
||||
# Linux (arm_64)
|
||||
- id: linux-arm64
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
env:
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
- CXX=aarch64-linux-gnu-g++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
|
||||
# Linux (arm_v7)
|
||||
- id: linux-arm7
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm
|
||||
goarm:
|
||||
- '7'
|
||||
env:
|
||||
- CC=arm-linux-gnueabihf-gcc
|
||||
- CXX=arm-linux-gnueabihf-g++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
|
||||
## ----------------------
|
||||
## macOS
|
||||
## ----------------------
|
||||
|
||||
# Darwin (amd_64)
|
||||
- id: darwin-amd64
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
|
||||
# Darwin (arm_64)
|
||||
- id: darwin-arm64
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- arm64
|
||||
env:
|
||||
- CC=oa64-clang
|
||||
- CXX=oa64-clang++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
|
||||
## ----------------------
|
||||
## Win
|
||||
## ----------------------
|
||||
|
||||
# Win (amd_64)
|
||||
- id: windows-amd64
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
flags:
|
||||
# https://go-review.googlesource.com/c/go/+/224588/
|
||||
# https://github.com/ArtalkJS/Artalk/issues/35
|
||||
&win_common_flags |
|
||||
-tags=timetzdata
|
||||
|
||||
# Win (arm_64)
|
||||
- id: windows-arm64
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- arm64
|
||||
env:
|
||||
- CC=/llvm-mingw/bin/aarch64-w64-mingw32-gcc
|
||||
- CXX=/llvm-mingw/bin/aarch64-w64-mingw32-g++
|
||||
binary: "{{.ProjectName}}"
|
||||
main: ./main.go
|
||||
ldflags: *common_ldflags
|
||||
flags: *win_common_flags
|
||||
|
||||
archives:
|
||||
- id: artalk
|
||||
builds:
|
||||
- linux-amd64
|
||||
- darwin-amd64
|
||||
- windows-amd64
|
||||
- windows-arm64
|
||||
- linux-arm7
|
||||
- linux-arm64
|
||||
- darwin-arm64
|
||||
name_template: "{{.ProjectName}}_v{{.Version}}_{{.Os}}_{{.Arch}}{{.Arm}}"
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- README*
|
||||
- LICENSE*
|
||||
- artalk.yml
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
name_template: "{{.Version}}-SNAPSHOT-{{.ShortCommit}}"
|
||||
|
||||
# changelog:
|
||||
# sort: asc
|
||||
# filters:
|
||||
# exclude:
|
||||
# - '^docs:'
|
||||
# - '^test:'
|
||||
# - '^chore:'
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: ArtalkJS
|
||||
name: Artalk
|
||||
prerelease: auto
|
||||
draft: false
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
.history
|
||||
.editorconfig
|
||||
|
||||
# git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# tests
|
||||
test
|
||||
coverage
|
||||
|
||||
# build tools
|
||||
.travis.yml
|
||||
.jenkins.yml
|
||||
.codeclimate.yml
|
||||
|
||||
# linters
|
||||
.jscsrc
|
||||
.jshintrc
|
||||
.eslintrc*
|
||||
|
||||
# vite configs
|
||||
vite*.config*
|
||||
|
||||
# develop tool configs
|
||||
.prettierrc
|
||||
.eslintrc.js
|
||||
.eslintignore
|
||||
postcss.config.js
|
||||
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
# local files
|
||||
deploy
|
||||
local
|
||||
CNAME
|
||||
index.html
|
||||
#dist
|
||||
|
||||
.github
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"singleQuote": true,
|
||||
"htmlWhitespaceSensitivity": "ignore"
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- lts/*
|
||||
|
||||
script:
|
||||
- yarn run lint
|
||||
- yarn run build
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Artalk for Debugging",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/bin/artalk",
|
||||
"preLaunchTask": "Build for debugging",
|
||||
"args": ["server", "-c", "${workspaceFolder}/data/artalk.yml"]
|
||||
}
|
||||
],
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"eslint.workingDirectories": ["./ui"],
|
||||
"cSpell.words": [
|
||||
"akismet",
|
||||
"artalk",
|
||||
"artran",
|
||||
"artrans",
|
||||
"artransfer",
|
||||
"gomail",
|
||||
"Goreleaser",
|
||||
"gorm",
|
||||
"ldflags",
|
||||
"lfshook",
|
||||
"Metas",
|
||||
"pgsql",
|
||||
"sendmail",
|
||||
"sqlite",
|
||||
"pkged"
|
||||
]
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build for debugging",
|
||||
"type": "shell",
|
||||
"command": "make build-debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
|
||||
## [v2.7.1](https://github.com/ArtalkJS/Artalk/compare/v2.7.0...v2.7.1) (2023-12-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** generate source maps to ease debugging
|
||||
* **cout-widget:** `loadCountWidget` issue
|
||||
* **ui:** static method export issue in JS module ([#669](https://github.com/ArtalkJS/Artalk/issues/669))
|
||||
|
||||
### Documentation
|
||||
|
||||
* **i18n:** add translation of update notice
|
||||
|
||||
|
||||
## [v2.7.0](https://github.com/ArtalkJS/Artalk/compare/v2.6.4...v2.7.0) (2023-12-17)
|
||||
|
||||
### Features
|
||||
|
||||
* **ui:** support `scrollRelativeTo` config option
|
||||
* **ui/test:** add end-to-end (e2e) testing using Playwright
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api/img-upload:** `public_path` config enables the use of full url ([#659](https://github.com/ArtalkJS/Artalk/issues/659)) ([#664](https://github.com/ArtalkJS/Artalk/issues/664))
|
||||
* **lifecycle:** create multi-instances at the same time ([#660](https://github.com/ArtalkJS/Artalk/issues/660)) ([#663](https://github.com/ArtalkJS/Artalk/issues/663))
|
||||
* **notify_pusher:** panic when admin ids array is empty ([#634](https://github.com/ArtalkJS/Artalk/issues/634))
|
||||
* **style/list:** replace `float` to `text-align` in footer part ([#619](https://github.com/ArtalkJS/Artalk/issues/619))
|
||||
* **ui:** scroll to the center of the viewport issue
|
||||
* **ui/conf:** sanitize `noComment` conf option for security ([#624](https://github.com/ArtalkJS/Artalk/issues/624))
|
||||
* **ui/dark-mode:** `setDarkMode` cannot save to instance config ([#661](https://github.com/ArtalkJS/Artalk/issues/661))
|
||||
* **ui/editor:** fix position of the comment box when replying ([#643](https://github.com/ArtalkJS/Artalk/issues/643)) ([#648](https://github.com/ArtalkJS/Artalk/issues/648))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **cache:** implement GC feature for simple_cache pkg ([#656](https://github.com/ArtalkJS/Artalk/issues/656))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **editor:** refactor editor plugin manager ([#609](https://github.com/ArtalkJS/Artalk/issues/609))
|
||||
* **editor:** refactor plug kit and events ([#613](https://github.com/ArtalkJS/Artalk/issues/613))
|
||||
* **ui:** reduce coupling with `ContextApi`
|
||||
* **ui:** separate standalone admin-only-elem checker
|
||||
* **ui/api:** loose coupling between `Api` and `Context`
|
||||
* **ui/conf:** loose coupling between config and list fetch
|
||||
* **ui/event:** refactor core event manager ([#611](https://github.com/ArtalkJS/Artalk/issues/611))
|
||||
* **ui/layer:** better layer implements and independence ([#662](https://github.com/ArtalkJS/Artalk/issues/662))
|
||||
* **ui/list:** separate list into standalone components ([#618](https://github.com/ArtalkJS/Artalk/issues/618))
|
||||
* **ui/marked:** separate markdown related codes
|
||||
* **ui/marked:** loose coupling with `marked` func
|
||||
* **ui/plugin:** further divide functionality into plugins ([#615](https://github.com/ArtalkJS/Artalk/issues/615))
|
||||
* **ui/stat:** losing coupling of `CountWidget`
|
||||
* **ui/types:** better `ArtalkType` import and export ([#620](https://github.com/ArtalkJS/Artalk/issues/620))
|
||||
|
||||
### Documentation
|
||||
|
||||
* revise and add more examples
|
||||
* provide clearer and more detailed instructions in CONTRIBUTING.md
|
||||
* **conf:** disable `frontend.uaBadge` config option by default
|
||||
* **deploy:** add deployment guide for render.com ([#649](https://github.com/ArtalkJS/Artalk/issues/649))
|
||||
* **import:** update the examples in import-framework.md ([#665](https://github.com/ArtalkJS/Artalk/issues/665))
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
|
||||
The following top-level functions exported by the 'artalk' npm package have been deprecated: `Artalk.update`, `Artalk.reload`, and `Artalk.destroy`. These methods now require invocation on an instance created by either `Artalk.init` or `new Artalk`. Please utilize instance-level methods instead, such as `artalkInstance.update`. For more information, refer to [the documentation](https://artalk.js.org/guide/frontend/import-framework.html). The update was implemented to enable the concurrent creation of multiple instances, adapting to situations where Vue components are simultaneously referenced across various pages. Initially, only a singular instance was permitted to mitigate memory leak concerns. However, this proved limiting for scenarios involving the caching of multiple component instances through 'keep-alive.' To better suit intricate SPA application needs, the choice was made to permit the creation of multiple independent instances. It's crucial to remember to manually invoke the `artalk.destroy` method when releasing components to avoid memory leaks (Issue [#660](https://github.com/ArtalkJS/Artalk/issues/660)).
|
||||
|
||||
|
||||
## [v2.6.4](https://github.com/ArtalkJS/Artalk/compare/v2.6.3...v2.6.4) (2023-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **go:** upgrade golang to v1.21.3 and some deps
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor:** fix duplicate event binding when update conf ([#605](https://github.com/ArtalkJS/Artalk/issues/605))
|
||||
* **email:** sender not initialized correctly
|
||||
* **style:** fix copyright text overlay on send button ([#607](https://github.com/ArtalkJS/Artalk/issues/607))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **anti_spam:** refactor anti_spam pkg and add unit test ([#589](https://github.com/ArtalkJS/Artalk/issues/589))
|
||||
|
||||
|
||||
## [v2.6.3](https://github.com/ArtalkJS/Artalk/compare/v2.6.2...v2.6.3) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** add `db.ssl` to enable db ssl mode ([#587](https://github.com/ArtalkJS/Artalk/issues/587))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **notify_tpl:** fix template render result in notify case
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **config:** add error message for timezone setting ([#586](https://github.com/ArtalkJS/Artalk/issues/586))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **renderer:** abstract func of template renderer for multi-cases ([#585](https://github.com/ArtalkJS/Artalk/issues/585))
|
||||
|
||||
### Documentation
|
||||
|
||||
* new landing page
|
||||
|
||||
|
||||
## [v2.6.2](https://github.com/ArtalkJS/Artalk/compare/v2.6.1...v2.6.2) (2023-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cmd:** remove app instance creation when mini boot ([#577](https://github.com/ArtalkJS/Artalk/issues/577))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **style:** modify `z-index` less than 5 ([#578](https://github.com/ArtalkJS/Artalk/issues/578))
|
||||
|
||||
|
||||
## [v2.6.1](https://github.com/ArtalkJS/Artalk/compare/v2.6.0...v2.6.1) (2023-09-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **captcha:** image captcha base64 response issue ([#575](https://github.com/ArtalkJS/Artalk/issues/575))
|
||||
|
||||
### Documentation
|
||||
|
||||
* **config:** add info about env variables ([#566](https://github.com/ArtalkJS/Artalk/issues/566))
|
||||
* **extras:** add deploy guide for railway.app ([#567](https://github.com/ArtalkJS/Artalk/issues/567))
|
||||
|
||||
|
||||
## [v2.6.0](https://github.com/ArtalkJS/Artalk/compare/v2.5.5...v2.6.0) (2023-09-01)
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** support load env variables as config ([#564](https://github.com/ArtalkJS/Artalk/issues/564))
|
||||
* **go:** upgrade golang to v1.21.0 and some deps ([#543](https://github.com/ArtalkJS/Artalk/issues/543))
|
||||
* **hook:** add hook pkg
|
||||
* **simple_cache:** a simple thread-safe cache with sync.Map
|
||||
* **utils:** add common random utils
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cache:** child comment ids store after comment updated
|
||||
* **cmd:** fix parse root cmd global flags
|
||||
* **config:** fix ip region patch
|
||||
* **db:** fix dsn strings for mysql connections ([#541](https://github.com/ArtalkJS/Artalk/issues/541))
|
||||
* **limiter:** limiter log ignore options method req
|
||||
* **limiter:** fix limiter always mode
|
||||
* **log:** add missing log for web server listen func ([#556](https://github.com/ArtalkJS/Artalk/issues/556))
|
||||
* **ui:** fix plug cannot disable issue ([#563](https://github.com/ArtalkJS/Artalk/issues/563))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **cmd:** support command mini boot mode
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **CI:** replace using `docker/metadata-action` for the docker push ([#545](https://github.com/ArtalkJS/Artalk/issues/545))
|
||||
* **CI:** replace renovate with github dependabot ([#546](https://github.com/ArtalkJS/Artalk/issues/546))
|
||||
* **anti_spam:** refactor anti_spam pkg
|
||||
* **artransfer:** refactor artransfer pkg
|
||||
* **cache:** remove cached fields in struct to save memory ([#555](https://github.com/ArtalkJS/Artalk/issues/555))
|
||||
* **cache:** cache code tidy-up
|
||||
* **cache:** refactor cache pkg
|
||||
* **cache:** refactor cache pkg
|
||||
* **cache:** refactor cache pkg
|
||||
* **captcha:** refactor captcha pkg
|
||||
* **cloud:** refactor cloud pkg
|
||||
* **cmd:** refactor cmd pkg
|
||||
* **conf:** better config.NewFromFile with error handle
|
||||
* **config:** refactor config pkg
|
||||
* **core:** refactor core pkg
|
||||
* **core:** better AppService func with error handle
|
||||
* **cors:** refactor cors middleware
|
||||
* **dao:** refactor dao pkg
|
||||
* **dao_cache:** add dao_cache pkg
|
||||
* **db:** refactor db pkg
|
||||
* **email:** refactor email pkg
|
||||
* **i18n:** refactor i18n pkg
|
||||
* **ip_region:** refactor ip_region pkg
|
||||
* **limiter:** add limiter pkg
|
||||
* **log:** create common log pkg
|
||||
* **main:** refactor main.go
|
||||
* **notify:** add notify_pusher pkg
|
||||
* **server/common:** refactor server/common pkg
|
||||
* **server/handler:** refactor server/handler pkg
|
||||
* **server/middleware:** refactor server/middleware pkg
|
||||
* **test:** create common test pkg
|
||||
|
||||
### Documentation
|
||||
|
||||
* **extras:** add deploy guide for fly.io ([#520](https://github.com/ArtalkJS/Artalk/issues/520))
|
||||
|
||||
|
||||
## [v2.5.5](https://github.com/ArtalkJS/Artalk/compare/v2.5.4...v2.5.5) (2023-05-10)
|
||||
|
||||
### Features
|
||||
|
||||
* **build:** add embedded fe lite and i18n scripts ([#493](https://github.com/ArtalkJS/Artalk/issues/493)) ([#505](https://github.com/ArtalkJS/Artalk/issues/505))
|
||||
* **go:** upgrade golang to v1.20.4 and some deps ([#506](https://github.com/ArtalkJS/Artalk/issues/506))
|
||||
* **gravatar:** add `gravatar.params` config option ([#508](https://github.com/ArtalkJS/Artalk/issues/508))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** comment IP data loss after editing ([#504](https://github.com/ArtalkJS/Artalk/issues/504))
|
||||
* **editor:** prevent editor reset on comment failure ([#507](https://github.com/ArtalkJS/Artalk/issues/507))
|
||||
|
||||
### BREAKING CHANGE
|
||||
|
||||
|
||||
The `gravatar.default` config option has been removed. Please use `gravatar.params` instead. The default value for `gravatar.params` is now `"d=mp&s=240"`.
|
||||
|
||||
|
||||
## [v2.5.4](https://github.com/ArtalkJS/Artalk/compare/v2.5.3...v2.5.4) (2023-04-09)
|
||||
|
||||
### Features
|
||||
|
||||
* **go:** upgrade golang to v1.20.3 and some deps ([#485](https://github.com/ArtalkJS/Artalk/issues/485))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **CI:** upgrade and pin node.js and pnpm version ([#484](https://github.com/ArtalkJS/Artalk/issues/484))
|
||||
|
||||
### Documentation
|
||||
|
||||
* update swagger docs and some deps
|
||||
|
||||
|
||||
## [v2.5.3](https://github.com/ArtalkJS/Artalk/compare/v2.5.2...v2.5.3) (2023-04-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api/comment_add:** SIGSEGV crash in comment_add ([#478](https://github.com/ArtalkJS/Artalk/issues/478)) ([#481](https://github.com/ArtalkJS/Artalk/issues/481))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **ip_region:** add db file existence check and error message ([#482](https://github.com/ArtalkJS/Artalk/issues/482))
|
||||
|
||||
|
||||
## [v2.5.2](https://github.com/ArtalkJS/Artalk/compare/v2.5.1...v2.5.2) (2023-03-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ui:** destroy instance when calling init func twice
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **notify:** shorten the length of notify key ([#466](https://github.com/ArtalkJS/Artalk/issues/466))
|
||||
* **style:** add `!important` for white-space css in code block ([#467](https://github.com/ArtalkJS/Artalk/issues/467))
|
||||
|
||||
|
||||
## [v2.5.1](https://github.com/ArtalkJS/Artalk/compare/v2.5.0...v2.5.1) (2023-03-16)
|
||||
|
||||
### Features
|
||||
|
||||
* **go:** upgrade golang to v1.20.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ui/count-widget:** context api undefined issue ([#464](https://github.com/ArtalkJS/Artalk/issues/464))
|
||||
* **ui/i18n:** duplicate packaging built-in language in the external script
|
||||
* **ui/paginator:** showErr func call issue
|
||||
* **ui/sort-dropdown:** dropdown menu disappears after call reload func ([#461](https://github.com/ArtalkJS/Artalk/issues/461))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **anti_spam/aliyun:** accessing aliyun green text api without sdk ([#459](https://github.com/ArtalkJS/Artalk/issues/459))
|
||||
* **ui/user:** modify user to standalone module ([#463](https://github.com/ArtalkJS/Artalk/issues/463))
|
||||
|
||||
|
||||
## [v2.5.0](https://github.com/ArtalkJS/Artalk/compare/v2.4.4...v2.5.0) (2023-03-10)
|
||||
|
||||
### Features
|
||||
|
||||
* migrate from `echo` to `go-fiber`
|
||||
* upgrade go to v1.20.1
|
||||
* display IP region of comment ([#418](https://github.com/ArtalkJS/Artalk/issues/418)) ([#447](https://github.com/ArtalkJS/Artalk/issues/447))
|
||||
* docker ci add support for building arm64 wheels
|
||||
* more functions to handle artalk lifecycle ([#426](https://github.com/ArtalkJS/Artalk/issues/426))
|
||||
* **ui:** add some static methods
|
||||
* **ui/height_limit:** support scrollable height limit area ([#451](https://github.com/ArtalkJS/Artalk/issues/451))
|
||||
* **ui/sidebar:** add dark mode support ([#450](https://github.com/ArtalkJS/Artalk/issues/450))
|
||||
* **captcha:** add support for reCAPTCHA and hCaptcha ([#456](https://github.com/ArtalkJS/Artalk/issues/456))
|
||||
* **captcha:** support turnstile captcha by cloudflare ([#453](https://github.com/ArtalkJS/Artalk/issues/453))
|
||||
* **i18n:** add i18n support for backend ([#343](https://github.com/ArtalkJS/Artalk/issues/343))
|
||||
* **i18n:** translations for backend ([#344](https://github.com/ArtalkJS/Artalk/issues/344))
|
||||
* **i18n:** add i18n support for sidebar ([#353](https://github.com/ArtalkJS/Artalk/issues/353))
|
||||
* **i18n:** add zh-TW i18n translation for sidebar and app
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ui:** hash goto function check condition issue
|
||||
* **ui/conf:** avoid some conf overrides frontend from the backend ([#449](https://github.com/ArtalkJS/Artalk/issues/449))
|
||||
* **ui/editor:** disable img upload cannot hide its btn
|
||||
* **ui/i18n:** subscribe event priority issue
|
||||
* **ui/sidebar:** array type of preference initial data issue
|
||||
* **ui/sidebar:** array type config option cannot be shown
|
||||
* **ui/sidebar:** boolean type setting option save issue ([#431](https://github.com/ArtalkJS/Artalk/issues/431)) ([#444](https://github.com/ArtalkJS/Artalk/issues/444))
|
||||
* **ui/sidebar:** setting item save follow type of template
|
||||
* **lint:** add tsc check before vite compile ([#440](https://github.com/ArtalkJS/Artalk/issues/440))
|
||||
* **email:** duplicate sending with multiple admins using same email addrs ([#375](https://github.com/ArtalkJS/Artalk/issues/375))
|
||||
* **email:** email queue initialization issue ([#374](https://github.com/ArtalkJS/Artalk/issues/374))
|
||||
* **email:** failback to `email.mail_tpl` if `admin_notify.email.mail_tpl` is empty
|
||||
* `timeAgo` function does not display the now
|
||||
* add `.npmignore` to fix NPM publish inclusion issue
|
||||
* sidebar navigation sorting ([#361](https://github.com/ArtalkJS/Artalk/issues/361))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* improve some css styles
|
||||
* add graceful shutdown
|
||||
* **conf/i18n:** detect and change locale when config file contains chinese
|
||||
* **ui/list:** remove useless function call
|
||||
* **ui/sidebar:** improve sidebar i18n
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* bump to monorepo
|
||||
* renamed from artalk-go to artalk
|
||||
* http origin checker
|
||||
* abstract email service
|
||||
* project package structure
|
||||
* remove version two-way check ([#452](https://github.com/ArtalkJS/Artalk/issues/452))
|
||||
* build scripts and CI tests
|
||||
* replace pkger with go:embed
|
||||
* launch with vscode debugger
|
||||
* **CI:** one-key site creating with artalk integrated
|
||||
* **CI:** improve build and release workflows ([#358](https://github.com/ArtalkJS/Artalk/issues/358))
|
||||
* **captcha:** abstract captcha service ([#455](https://github.com/ArtalkJS/Artalk/issues/455))
|
||||
* **comment:** separate comment ui renders from single file ([#427](https://github.com/ArtalkJS/Artalk/issues/427))
|
||||
* **style:** convert to use Sass as a style interpreter ([#439](https://github.com/ArtalkJS/Artalk/issues/439))
|
||||
* **ui:** automatic dependency injection ([#429](https://github.com/ArtalkJS/Artalk/issues/429))
|
||||
* **ui/checker:** simplify checker lifecycle function param table ([#428](https://github.com/ArtalkJS/Artalk/issues/428))
|
||||
* **ui/dark-mode:** separate dark mode logic into its own module ([#430](https://github.com/ArtalkJS/Artalk/issues/430))
|
||||
* **ui/editor:** modify editor ui to standalone module ([#441](https://github.com/ArtalkJS/Artalk/issues/441))
|
||||
* **ui/editor:** change functions of editor to standalone modules ([#443](https://github.com/ArtalkJS/Artalk/issues/443))
|
||||
* **ui/height-limit:** modify height limit function to standalone module ([#435](https://github.com/ArtalkJS/Artalk/issues/435))
|
||||
* **ui/i18n:** improve i18n function to standalone module ([#434](https://github.com/ArtalkJS/Artalk/issues/434))
|
||||
* **ui/list:** modify list pagination to standalone module ([#437](https://github.com/ArtalkJS/Artalk/issues/437))
|
||||
* **page/fetch:** remove goquery dependency when extracting page data ([#442](https://github.com/ArtalkJS/Artalk/issues/442))
|
||||
* **anti_spam/qcloud-tms:** implement qcloud tms api without sdk ([#438](https://github.com/ArtalkJS/Artalk/issues/438))
|
||||
|
||||
### Documentation
|
||||
|
||||
* migrate ArtalkJS/Docs to monorepo docs
|
||||
* add `CODE_OF_CONDUCT.md`
|
||||
* add simplified README for artalk npm package
|
||||
* add translation section to `CONTRIBUTING.md`
|
||||
* fix wrong config value ([#371](https://github.com/ArtalkJS/Artalk/issues/371))
|
||||
* fix broken links ([#364](https://github.com/ArtalkJS/Artalk/issues/364))
|
||||
* add open api ([#360](https://github.com/ArtalkJS/Artalk/issues/360))
|
||||
* add `Project Structure` section to `CONTRIBUTING.md`
|
||||
* refine and add frontend api docs
|
||||
* update setup-example-site.sh script usage
|
||||
* init artalk with new frontend api
|
||||
* **deploy:** add `restart=always` for docker to auto restart ([#425](https://github.com/ArtalkJS/Artalk/issues/425))
|
||||
* **extras:** add deploy guide for vuepress ([#436](https://github.com/ArtalkJS/Artalk/issues/436))
|
||||
@@ -0,0 +1,131 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <artalkjs@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
# Developer Contributing Guide
|
||||
|
||||
This guide is for developers who want to contribute to the project.
|
||||
|
||||
## Development Environment
|
||||
|
||||
To develop Artalk, including the frontend and backend, you need to install the following tools:
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) (>= 18.12.0)
|
||||
- [Go](https://golang.org/) (>= 1.21)
|
||||
- [Docker](https://www.docker.com/) (>= 20.10.0) (optional)
|
||||
- [Docker Compose](https://docs.docker.com/compose/) (>= 1.29.0) (optional)
|
||||
|
||||
Then we'll setup the development environment.
|
||||
|
||||
### Get the source code
|
||||
|
||||
Run the following command to clone the repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/ArtalkJS/Artalk
|
||||
```
|
||||
|
||||
It is recommended to fork the repository first, and then clone the forked repository.
|
||||
|
||||
Enter the directory:
|
||||
|
||||
```sh
|
||||
cd Artalk
|
||||
```
|
||||
|
||||
### Build Backend
|
||||
|
||||
First, you need to install the dependencies for the backend written in Go. Simply run the `make install` command to install the dependencies.
|
||||
|
||||
Then, run the `make dev` to build and run `./bin/artalk`, and you can pass startup parameters to the program using `ARGS="version" make dev`.
|
||||
|
||||
This will build the backend with debugging symbols. The binary file will be placed under the `./bin` directory.
|
||||
|
||||
The backend program will run by default on port `23366`. You can access it through a browser at `http://localhost:23366`. It's recommended not to change this port number for testing the backend program.
|
||||
|
||||
### Build Frontend
|
||||
|
||||
First, you need to install the dependencies for the frontend. Simply run the `cd ui && pnpm install` command to install the dependencies.
|
||||
|
||||
Then, run the `cd ui && pnpm dev` to build and run the frontend, and you can pass startup parameters to the program using `ARGS="--port 5173" pnpm dev`.
|
||||
|
||||
The frontend program will run by default on port `5173`, and you can access it in a browser at `http://localhost:5173`. The frontend testing client will, by default, request the backend on port `23366`, so it's essential to keep the backend on this port.
|
||||
|
||||
The frontend program is divided into the main program and a sidebar program, with the sidebar program running on a separate port, which is `23367`.
|
||||
|
||||
### Development Workflow
|
||||
|
||||
In most cases, to set up a complete development instance, you need to navigate to the Artalk Repo directory and then execute `make dev`. This will run the backend on port `23366`. Then, execute `cd ui && pnpm dev`, which will run the frontend on port `5173`. You can optionally execute `pnpm dev:sidebar` to run the sidebar frontend on port `23367`. For frontend development, you need to access `http://localhost:5173` in your browser to perform your development and testing.
|
||||
|
||||
When you make changes to the frontend code, you can build the complete frontend program using `cd ui && pnpm build:all`. The JavaScript and CSS code can be found in `ui/packages/artalk/dist`.
|
||||
|
||||
When you make changes to the backend code, running `make all` will build the complete backend program. Note that since the backend program also embeds the frontend code, the `scripts/build-frontend.sh` script will run during backend program building, which includes the embedded frontend main program and sidebar frontend program. If you are interested, you can explore the complete frontend build process in the `Makefile` code.
|
||||
|
||||
Additionally, there is automated CI on GitHub for building. You can find the relevant code in the `.github/workflows` directory.
|
||||
|
||||
### Optional: Use a One-Key Script to Run a Demo Site
|
||||
|
||||
When you access `http://localhost:5173` during frontend development, you will get a minimalist interface that only contains the Artalk program interface and not a real blog environment. If you want to make testing closer to a real environment, you can create a demo blog site using the following steps.
|
||||
|
||||
Run the following command:
|
||||
|
||||
```sh
|
||||
./scripts/setup-example-site.sh
|
||||
```
|
||||
|
||||
This script sets up a local example site for testing at `data` folder, with Artalk integrated into its theme.
|
||||
|
||||
After running this script, run:
|
||||
|
||||
```sh
|
||||
./bin/artalk server -c ./data/artalk.yml
|
||||
```
|
||||
|
||||
to start the artalk server.
|
||||
|
||||
And open <http://localhost:1313/> in your browser to view the example site.
|
||||
|
||||
Here is the default admin account (only created in test mode):
|
||||
|
||||
```yaml
|
||||
name: "admin"
|
||||
email: "admin@test.com"
|
||||
password: "admin"
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The backend Go program can be tested by running `make test` for unit testing. The test results will be outputted in the terminal. You can also execute `test-coverage` to check the code test coverage.
|
||||
|
||||
Frontend testing is conducted using Playwright for end-to-end (E2E) testing. To start the E2E testing, run `make test-frontend-e2e`.
|
||||
|
||||
Both frontend and backend testing are automated and will be performed during Git pull requests and as part of the build process using CircleCI or GitHub Actions.
|
||||
|
||||
## Project Structure
|
||||
|
||||
Artalk is a monorepo project, which means all the source code is in the same repository. However, the frontend and backend are separated. The frontend part is located in `./ui` directory.
|
||||
|
||||
- `bin/` - The compiled binary files. This directory is ignored by git.
|
||||
- `cmd/` - The source code for the command line tools.
|
||||
- `conf/` - The sample configuration files.
|
||||
- `docs/` - The documentation site source code.
|
||||
- `i18n/` - The translation files.
|
||||
- `internal/` - The internal packages.
|
||||
- `data/` - The local data. This directory is ignored by git.
|
||||
- `public/` - The static files. Built frontend files will be copied here.
|
||||
- `scripts/` - The scripts for development.
|
||||
- `server/` - The source code for the server.
|
||||
- `ui/` - The source code for the frontend.
|
||||
|
||||
## Translation
|
||||
|
||||
Artalk aims to be a multilingual project. If you would like to contribute to the translation, here are some tips:
|
||||
|
||||
If you just write some new features or do some fixes/refactoring, use the following command to generate the translation template
|
||||
|
||||
```sh
|
||||
go run ./internal/i18n/gen -w . -d .
|
||||
```
|
||||
|
||||
If you're not a programmer and would like to help us improve the translation, you can edit the translation files directly in the `.i18n' directory and then submit a pull request.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
### build Artalk
|
||||
FROM golang:1.21.3-alpine3.18 as builder
|
||||
|
||||
WORKDIR /source
|
||||
|
||||
# install tools
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache make git gcc musl-dev nodejs bash npm\
|
||||
&& npm install -g pnpm@8.12.1
|
||||
|
||||
# download go deps
|
||||
# (cache by separating the downloading of deps)
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# copy source code
|
||||
COPY . .
|
||||
|
||||
# build
|
||||
RUN set -ex \
|
||||
&& export VERSION=$(git describe --tags --abbrev=0) \
|
||||
&& export COMMIT_SHA=$(git rev-parse --short HEAD) \
|
||||
&& make build
|
||||
|
||||
### build final image
|
||||
FROM alpine:3.18
|
||||
|
||||
# we set the timezone `Asia/Shanghai` by default, you can be modified
|
||||
# by `docker build --build-arg="TZ=Other_Timezone ..."`
|
||||
ARG TZ="Asia/Shanghai"
|
||||
|
||||
ENV TZ ${TZ}
|
||||
|
||||
COPY --from=builder /source/bin/artalk /artalk
|
||||
|
||||
RUN apk add --no-cache bash tzdata \
|
||||
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
|
||||
&& echo ${TZ} > /etc/timezone
|
||||
|
||||
# move runner script to `/usr/bin/` and create alias
|
||||
COPY scripts/docker-artalk-runner.sh /usr/bin/artalk
|
||||
RUN chmod +x /usr/bin/artalk \
|
||||
&& ln -s /usr/bin/artalk /usr/bin/artalk-go
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
||||
COPY docker-entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
# expose Artalk default port
|
||||
EXPOSE 23366
|
||||
|
||||
CMD ["server", "--host", "0.0.0.0", "--port", "23366"]
|
||||
@@ -1,674 +1,21 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-present, qwqcode and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,69 @@
|
||||
PKG_NAME := github.com/ArtalkJS/Artalk
|
||||
BIN_NAME := ./bin/artalk
|
||||
VERSION ?= $(shell git describe --tags --abbrev=0)
|
||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||
DEV_VERSION := dev-$(COMMIT_HASH)
|
||||
|
||||
HAS_RICHGO := $(shell which richgo)
|
||||
GOTEST ?= $(if $(HAS_RICHGO), richgo test, go test)
|
||||
ARGS ?= server
|
||||
|
||||
all: install build
|
||||
|
||||
install:
|
||||
go mod tidy
|
||||
|
||||
build: build-frontend
|
||||
go build \
|
||||
-ldflags "-s -w -X $(PKG_NAME)/internal/config.Version=$(VERSION) \
|
||||
-X $(PKG_NAME)/internal/config.CommitHash=$(COMMIT_HASH)" \
|
||||
-o $(BIN_NAME) \
|
||||
$(PKG_NAME)
|
||||
|
||||
build-frontend:
|
||||
./scripts/build-frontend.sh
|
||||
|
||||
run: all
|
||||
$(BIN_NAME) $(ARGS)
|
||||
|
||||
build-debug:
|
||||
@echo "Building Artalk $(VERSION) for debugging..."
|
||||
@go build \
|
||||
-ldflags "-X $(PKG_NAME)/internal/config.Version=$(VERSION) \
|
||||
-X $(PKG_NAME)/internal/config.CommitHash=$(COMMIT_HASH)" \
|
||||
-gcflags "all=-N -l" \
|
||||
-o $(BIN_NAME) \
|
||||
$(PKG_NAME)
|
||||
|
||||
dev: build-debug
|
||||
$(BIN_NAME) $(ARGS)
|
||||
|
||||
test:
|
||||
$(GOTEST) -timeout 20m ./internal/...
|
||||
|
||||
test-coverage:
|
||||
$(GOTEST) -cover ./...
|
||||
|
||||
test-coverage-html:
|
||||
$(GOTEST) -v -coverprofile=coverage.out ./...
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
update-i18n:
|
||||
go generate ./internal/i18n
|
||||
|
||||
update-swagger:
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
swag init -g server/server.go --output ./docs/swagger
|
||||
|
||||
docker-build:
|
||||
./scripts/docker-build.sh
|
||||
|
||||
docker-push:
|
||||
./scripts/docker-build.sh --push
|
||||
|
||||
test-frontend-e2e:
|
||||
./scripts/frontend-e2e-test.sh $(if $(REPORT), --show-report)
|
||||
|
||||
.PHONY: all install build build-frontend build-debug \
|
||||
dev test test-coverage test-coverage-html update-i18n \
|
||||
docker-build docker-push test-frontend-e2e;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/22412567/137740516-d9e97af0-fb3b-4dab-b331-671a9a2a3a63.png" alt="Artalk" width="100%">
|
||||
</p>
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
|
||||
> 🌌 A self-hosted comment system
|
||||
|
||||
[简体中文](./README.md) / [Documentation](https://artalk.js.org) / [Releases](https://github.com/ArtalkJS/Artalk/releases) / [Artalk](https://github.com/ArtalkJS/Artalk)
|
||||
|
||||
---
|
||||
|
||||
- 🍃 Lightweight (~30kB)
|
||||
- 👨👧👦 Secure (Self-hosted)
|
||||
- 🐳 Easy to use (Very Easy)
|
||||
- 🍱 Golang backend (Fast and Cross Platform)
|
||||
- 🌊 TypeScript × Vanilla × Vite (No Dependencies)
|
||||
|
||||
## Features
|
||||
|
||||
- Sidebar: Multi-site centralized management
|
||||
- Notification Center: Red badge alert / Mark as read
|
||||
- Verification: User Verification Badge / Password access
|
||||
- Moderation: Anti-spam detection / Captcha frequency limit
|
||||
- Emoticons: Insert emoji / Quickly import presets
|
||||
- Email Notify: Template customization / Send to multi-admin
|
||||
- Site Isolation: Multi-site management / Admin assignment
|
||||
- Page Management: Provide page title / Easy to look up
|
||||
- Image Upload: Upload to localhost / various remote image host
|
||||
- Private Space Mode: Only visible to yourself / Message board
|
||||
- Multiple Msg Pushers: Support Telegram / Slack / LINE
|
||||
- Nested Comments: Switchable to Flat Mode
|
||||
- Comment Vote: For or against comments
|
||||
- Comment Sort: Sort by popularity or time
|
||||
- Comment PIN: Pin important comments
|
||||
- Track only author: Show only author's comments
|
||||
- Asynchronous: Send comments without waiting
|
||||
- Infinite Scrolling: Provide various comment content pagination custom
|
||||
- AutoSave: A anti-lost editor
|
||||
- AutoFill: Autofill user profile
|
||||
- Preview: Real-time preview of markdown
|
||||
- Dark Mode: Prevents eye disease damage
|
||||
- Collapse Comment: I do not want you to see this comment
|
||||
- Data Backup: Prevent comment data loss
|
||||
- Data Migration: Switch back and forth between different commenting systems
|
||||
- Multiple comments on one page: Multiple comment areas on one page (seems useless
|
||||
- Markdown: Markdown syntax was supported by default
|
||||
- LaTex support: Import KaTex plugin to integrate LaTex parser for artalk
|
||||
- Using [Vite](https://github.com/vitejs/vite): The ultimate developer experience
|
||||
|
||||
## Getting Started
|
||||
|
||||
Reference to:[**Documentation**](https://artalk.js.org/guide/deploy.html)
|
||||
|
||||
```sh
|
||||
$ pnpm add artalk
|
||||
```
|
||||
|
||||
```ts
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http://your_domain/post/1', // Page Link
|
||||
pageTitle: 'The title of your page', // Page Title
|
||||
server: 'http://localhost:8080/api', // Server URL
|
||||
site: 'Site Name',
|
||||
locale: 'en'
|
||||
})
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```sh
|
||||
mkdir Artalk
|
||||
cd Artalk
|
||||
|
||||
vim docker-compose.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3.5"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
```
|
||||
|
||||
```sh
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
## Contributors
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/graphs/contributors)
|
||||
|
||||
## Supporters
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/stargazers)
|
||||
|
||||
## Feedback
|
||||
|
||||
Thanks for the help and feedback provided by the community, if you have good suggestions or comments, please go to [issues](https://github.com/ArtalkJS/Artalk/issues) to let us know at any time.
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/ArtalkJS/Artalk)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_shield)
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_large)
|
||||
@@ -1,83 +1,235 @@
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/22412567/137740516-d9e97af0-fb3b-4dab-b331-671a9a2a3a63.png" alt="Artalk" width="100%">
|
||||
<img src="https://user-images.githubusercontent.com/22412567/171680920-6e74b77c-c565-487b-bff1-4f94976ecbe7.png" alt="Artalk" width="100%">
|
||||
</p>
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://github.com/ArtalkJS/Artalk/commits/master)
|
||||
[](https://github.com/ArtalkJS/Artalk/issues)
|
||||
[](https://github.com/ArtalkJS/Artalk/pulls)
|
||||
[](https://travis-ci.com/ArtalkJS/Artalk)
|
||||
[](./LICENSE)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://hub.docker.com/r/artalk/artalk-go)
|
||||
[](https://goreportcard.com/report/github.com/ArtalkJS/Artalk)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
[](https://codecov.io/gh/ArtalkJS/Artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
|
||||
> 🌌 一款简洁的自托管评论系统 | A Selfhosted Comment System.
|
||||
> 🌌 Golang 自托管评论系统
|
||||
|
||||
前往:[“**官方文档**”](https://artalk.js.org)
|
||||
[English](./README.en.md) / [官方文档](https://artalk.js.org) / [最新版本](https://github.com/ArtalkJS/Artalk/releases)
|
||||
|
||||
---
|
||||
|
||||
- 轻量 (~30kB gzipped)
|
||||
- 自托管 (隐私安全)
|
||||
- 简单易上手 (防秃顶)
|
||||
- Markdown 语法 (通用性)
|
||||
- Golang 后端 (易部署 / 跨平台)
|
||||
- 🍃 轻量 (~30kB)
|
||||
- 👨👧👦 安全 (自托管)
|
||||
- 🐳 易上手 (防脱发)
|
||||
- 🍱 Golang 后端 (快速 / 跨平台)
|
||||
- 🌊 Vanilla × TypeScript × Vite (纯天然 / 无依赖)
|
||||
|
||||
## 特性
|
||||
|
||||
- 表 情 包 :统一管理 / 链接替换
|
||||
- 通知中心:红点标记 / 已读记录
|
||||
- 侧 边 栏 :支持多站点集中化管理
|
||||
- 通知中心:红点的标记 / 提及列表
|
||||
- 身份验证:徽标自定义 / 密码验证
|
||||
- 站点隔离:多站点管理 / 站点管理员用户分配
|
||||
- 评论审核:反垃圾检测 / 验证码 / 提交频率限制
|
||||
- 邮件提醒:模版自定义 / 多管理员通知
|
||||
- 树洞模式:仅自己可见 / 说说功能
|
||||
- 评论审核:反垃圾检测 / 频率限制
|
||||
- 表情符号:插入表情包 / 快速导入
|
||||
- 邮件提醒:模版自定义 / 多管理员
|
||||
- 站点隔离:管理员分配 / 多个站点
|
||||
- 页面管理:标题可显示 / 快速跳转
|
||||
- 无限层级:可切换为 “平铺模式”
|
||||
- 图片上传:上传到本地 / 多种图床
|
||||
- 多元推送:支持 钉钉 飞书 TG
|
||||
- 无限层级:可切换为平铺模式
|
||||
- 评论投票:赞同还是反对评论
|
||||
- 异步处理:邮件发送无需等待
|
||||
- 评论排序:按热度或时间排序
|
||||
- 评论置顶:重要消息置顶显示
|
||||
- 只看作者:仅显示作者的评论
|
||||
- 说说模式:仅自己可发布评论
|
||||
- 异步处理:发送评论无需等待
|
||||
- 滚动加载:评论内容分页处置
|
||||
- 自动保存:用户输入防丢功能
|
||||
- 自动填充:用户链接自动填充
|
||||
- 暗黑模式:防止眼部疾病伤害
|
||||
- 实时预览:评论内容实时预览
|
||||
- 夜间模式:防止眼部疾病伤害
|
||||
- 评论折叠:这个不打算给你康
|
||||
- 数据备份:防止评论数据丢失
|
||||
- 数据迁移:在不同评论系统之间来回切换
|
||||
- 一页多评:一页多个评论区(似乎没啥用
|
||||
- TypeScript & Vanilla:纯天然无添加 / 无需依赖
|
||||
- [Vite](https://github.com/vitejs/vite):开发者的极致体验
|
||||
- 数据迁移:快速切换评论系统
|
||||
- Markdown:默认支持 MD 语法
|
||||
- 支持 Latex:提供集成 Katex 插件
|
||||
- 使用 [Vite](https://github.com/vitejs/vite):属于开发者的极致体验
|
||||
|
||||
## 食用方针
|
||||
更多内容请查看:[**介绍文档**](https://artalk.js.org/guide/intro.html)
|
||||
|
||||
前往:[“**文档 · 部署**”](https://artalk.js.org/guide/deploy.html)
|
||||
## 安装
|
||||
|
||||
```sh
|
||||
$ yarn add artalk
|
||||
参考:[**部署文档**](https://artalk.js.org/guide/deploy.html)
|
||||
|
||||
```bash
|
||||
npm install artalk
|
||||
```
|
||||
|
||||
```ts
|
||||
import Artalk from 'artalk'
|
||||
|
||||
new Artalk({
|
||||
el: '#Comments',
|
||||
pageKey: '<页面链接>',
|
||||
pageTitle: '<页面标题>',
|
||||
server: '<后端地址>',
|
||||
site: '<站点名称>',
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
site: 'Artalk 的博客',
|
||||
server: 'https://artalk.example.com'
|
||||
})
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
--restart=always \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
vim docker-compose.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3.5"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
```
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
## Contributors
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/graphs/contributors)
|
||||
|
||||
## Supporters
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/stargazers)
|
||||
|
||||
## Repobeats Analytics
|
||||
|
||||

|
||||
|
||||
## 特别致谢
|
||||
|
||||
感谢社区提供的帮助与反馈,若有好的建议与意见欢迎前往 [ISSUES](https://github.com/ArtalkJS/Artalk/issues) 随时告知。
|
||||
|
||||
## TODOs
|
||||
|
||||
- [x] Golang 后端
|
||||
- [x] 多数据库支持
|
||||
- [x] SQLite
|
||||
- [x] MySQL
|
||||
- [x] Postgres
|
||||
- [x] SQLServer
|
||||
- [x] 多缓存数据库支持
|
||||
- [x] In-memory (内建缓存)
|
||||
- [x] Redis
|
||||
- [x] Memcache
|
||||
- [x] 多站点支持
|
||||
- [x] 评论获取分页
|
||||
- [x] 评论点赞投票
|
||||
- [x] 浏览量统计
|
||||
- [x] 评论分页加载
|
||||
- [x] 评论置顶 / 精华
|
||||
- [x] 评论排序 (热度 / 时间)
|
||||
- [x] 通知中心
|
||||
- [x] 提及
|
||||
- [x] 全部
|
||||
- [x] 我的
|
||||
- [x] 待审
|
||||
- [x] 管理员控制台
|
||||
- [x] 评论
|
||||
- [x] 页面
|
||||
- [x] 站点
|
||||
- [x] 用户
|
||||
- [x] 设置 (GUI)
|
||||
- [x] 数据导入 ([Artransfer](https://artalk.js.org/guide/transfer.html))
|
||||
- [x] Artrans
|
||||
- [x] WordPress
|
||||
- [x] Typecho ([插件](https://github.com/ArtalkJS/Artrans-Typecho) / [Artransfer-CLI](https://github.com/ArtalkJS/Artransfer-CLI))
|
||||
- [x] Valine
|
||||
- [x] Disqus
|
||||
- [x] Commento
|
||||
- [x] Waline ([Artransfer-CLI](https://github.com/ArtalkJS/Artransfer-CLI))
|
||||
- [x] Twikoo
|
||||
- [x] Artalk v1 (PHP)
|
||||
- [x] 数据导出
|
||||
- [x] 邮件多种发送方式
|
||||
- [x] SMTP
|
||||
- [x] 阿里云邮件
|
||||
- [x] 系统调用 sendmail
|
||||
- [x] 邮件多模板自定义
|
||||
- [x] 邮件异步队列发送
|
||||
- [ ] 队列持久化
|
||||
- [x] 用户已读标记
|
||||
- [x] 验证码
|
||||
- [x] 图片验证码
|
||||
- [x] [Turnstile](https://www.cloudflare.com/products/turnstile/)
|
||||
- [x] [ReCAPTCHA](https://www.google.com/recaptcha/about/)
|
||||
- [x] [hCaptcha](https://www.hcaptcha.com/)
|
||||
- [x] [极验](https://www.geetest.com/)滑动验证码
|
||||
- [x] 反垃圾
|
||||
- [x] [Akismet](https://akismet.com/)
|
||||
- [x] [阿里云内容安全](https://help.aliyun.com/document_detail/28417.html)
|
||||
- [x] [腾讯云内容安全](https://cloud.tencent.com/document/product/1124/64508)
|
||||
- [x] 关键字过滤
|
||||
- [x] 评论通知管理员 ([notify](https://github.com/nikoksr/notify))
|
||||
- [x] Telegram Bot
|
||||
- [x] 飞书 WebHook Bot
|
||||
- [x] 钉钉
|
||||
- [x] Bark
|
||||
- [x] Slack
|
||||
- [x] LINE
|
||||
- [x] 自定义 HTTP 回调
|
||||
- [ ] 命令行管理
|
||||
- [ ] 博客邮件订阅
|
||||
- [x] 用户鉴权机制
|
||||
- [x] 跨域非法请求阻止
|
||||
- [x] 全局验证码操作次数限制
|
||||
- [x] JWT 登陆状态验证
|
||||
- [x] 时区自定义
|
||||
- [x] 只看作者功能
|
||||
- [x] 图片上传
|
||||
- [x] 图片上传到图床 ([upgit](https://github.com/pluveto/upgit))
|
||||
- [ ] 图片管理
|
||||
- [ ] 附件上传 / 管理
|
||||
- [ ] 表情包统一管理
|
||||
- [ ] 导入表情包
|
||||
- [ ] 表情包图片地址控制
|
||||
- [ ] AT 提及 (@)
|
||||
- [ ] 评论话题 (#)
|
||||
- [ ] 评论标签分类系统
|
||||
- [ ] 主题样式更换
|
||||
- [ ] 规范化 API
|
||||
- [ ] 扩展中心
|
||||
- [ ] 开放用户注册
|
||||
- [ ] 第三方登录接入
|
||||
- [x] 多语言 / 国际化 (i18n)
|
||||
- [x] 一键升级
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/ArtalkJS/Artalk)
|
||||
|
||||
## License
|
||||
[GPL-3.0](./LICENSE)
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_shield)
|
||||
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/entity"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/ArtalkJS/Artalk/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func NewAdminCommand(app *ArtalkCmd) *cobra.Command {
|
||||
adminCmd := &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Create or edit an administrator account",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println(" " + i18n.T("Create admin account"))
|
||||
fmt.Println("--------------------------------")
|
||||
|
||||
username, email, password, err := credentials()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
findUser := app.Dao().FindUser(username, email)
|
||||
if !findUser.IsEmpty() {
|
||||
findUser.SetPasswordEncrypt(password)
|
||||
if err := app.Dao().UpdateUser(&findUser); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Info(i18n.T("{{name}} already exists", map[string]interface{}{"name": i18n.T("Account")}) +
|
||||
", " + i18n.T("Password updated"))
|
||||
return
|
||||
}
|
||||
|
||||
user := entity.User{
|
||||
Name: username,
|
||||
Email: email,
|
||||
IsAdmin: true,
|
||||
BadgeName: i18n.T("Admin"),
|
||||
BadgeColor: "#0083FF",
|
||||
}
|
||||
user.SetPasswordEncrypt(password)
|
||||
|
||||
if err := app.Dao().CreateUser(&user); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println(" Name: " + username)
|
||||
fmt.Println(" Mail: " + email)
|
||||
fmt.Println("--------------------------------")
|
||||
},
|
||||
}
|
||||
|
||||
return adminCmd
|
||||
}
|
||||
|
||||
func credentials() (string, string, string, error) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Print(i18n.T("Enter {{name}}", map[string]interface{}{"name": i18n.T("Username")}) + ": ")
|
||||
username, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
fmt.Print(i18n.T("Enter {{name}}", map[string]interface{}{"name": i18n.T("Email")}) + ": ")
|
||||
email, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if !utils.ValidateEmail(strings.TrimSpace(email)) {
|
||||
return "", "", "", fmt.Errorf("invalid email format")
|
||||
}
|
||||
|
||||
fmt.Print(i18n.T("Enter {{name}}", map[string]interface{}{"name": i18n.T("Password")}) + ": ")
|
||||
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Print(i18n.T("Retype {{name}}", map[string]interface{}{"name": i18n.T("Password")}) + ": ")
|
||||
byteRePassword, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
password := strings.TrimSpace(string(bytePassword))
|
||||
rePassword := strings.TrimSpace(string(byteRePassword))
|
||||
|
||||
if rePassword != password {
|
||||
return "", "", "", fmt.Errorf("inconsistent password input")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(username), strings.TrimSpace(email), password, nil
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/config"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Version = config.Version + `/` + config.CommitHash
|
||||
|
||||
var Banner = `
|
||||
________ ________ _________ ________ ___ ___ __
|
||||
|\ __ \|\ __ \|\___ ___\\ __ \|\ \ |\ \|\ \
|
||||
\ \ \|\ \ \ \|\ \|___ \ \_\ \ \|\ \ \ \ \ \ \/ /|_
|
||||
\ \ __ \ \ _ _\ \ \ \ \ \ __ \ \ \ \ \ ___ \
|
||||
\ \ \ \ \ \ \\ \| \ \ \ \ \ \ \ \ \ \____\ \ \\ \ \
|
||||
\ \__\ \__\ \__\\ _\ \ \__\ \ \__\ \__\ \_______\ \__\\ \__\
|
||||
\|__|\|__|\|__|\|__| \|__| \|__|\|__|\|_______|\|__| \|__|
|
||||
|
||||
Artalk (` + Version + `)
|
||||
|
||||
-> A Self-hosted Comment System.
|
||||
-> https://artalk.js.org
|
||||
`
|
||||
|
||||
type ArtalkCmd struct {
|
||||
*core.App
|
||||
|
||||
cfgFile string
|
||||
workDir string
|
||||
|
||||
RootCmd *cobra.Command
|
||||
}
|
||||
|
||||
const BootModeKey = "BootMode"
|
||||
|
||||
const (
|
||||
MODE_FULL_BOOT = "FULL_BOOT"
|
||||
MODE_MINI_BOOT = "MINI_BOOT"
|
||||
)
|
||||
|
||||
func New() *ArtalkCmd {
|
||||
cmd := &ArtalkCmd{
|
||||
RootCmd: &cobra.Command{
|
||||
Use: "artalk",
|
||||
Short: "Artalk: A self-hosted comment system",
|
||||
Long: Banner,
|
||||
Version: Version,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(Banner)
|
||||
fmt.Print("-------------------------------\n\n")
|
||||
fmt.Println("NOTE: add `-h` flag to show help about any command.")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cmd.RootCmd.SetVersionTemplate("Artalk ({{printf \"%s\" .Version}})\n")
|
||||
|
||||
// Parse base flags
|
||||
cmd.eagerParseFlags()
|
||||
|
||||
// Change work directory
|
||||
if cmd.workDir != "" {
|
||||
if err := os.Chdir(cmd.workDir); err != nil {
|
||||
log.Fatal("Working directory change error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Parses the global app flags before calling atk.RootCmd.Execute().
|
||||
// so we can have all flags ready for use on initialization.
|
||||
func (atk *ArtalkCmd) eagerParseFlags() error {
|
||||
atk.RootCmd.PersistentFlags().StringVarP(&atk.cfgFile, "config", "c", "", "config file path (defaults are './artalk.yml')")
|
||||
atk.RootCmd.PersistentFlags().StringVarP(&atk.workDir, "workdir", "w", "", "program working directory (defaults are './')")
|
||||
|
||||
return atk.RootCmd.ParseFlags(os.Args[1:])
|
||||
}
|
||||
|
||||
func (atk *ArtalkCmd) addCommand(cmd *cobra.Command) {
|
||||
originalPreRunFunc := cmd.PreRun
|
||||
|
||||
cmd.PreRun = func(cmd *cobra.Command, args []string) {
|
||||
// ================
|
||||
// APP Bootstrap
|
||||
// ================
|
||||
if cmd.Annotations[BootModeKey] != string(MODE_MINI_BOOT) {
|
||||
// Load config
|
||||
config, err := getConfig(atk.cfgFile)
|
||||
if err != nil {
|
||||
log.Fatal("Config fail: ", err)
|
||||
}
|
||||
|
||||
// Create new instance
|
||||
atk.App = core.NewApp(config)
|
||||
|
||||
// Bootstrap APP
|
||||
if err := atk.App.Bootstrap(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if originalPreRunFunc != nil {
|
||||
originalPreRunFunc(cmd, args) // extends original pre-run logic
|
||||
}
|
||||
}
|
||||
|
||||
atk.RootCmd.AddCommand(cmd)
|
||||
}
|
||||
|
||||
func (atk *ArtalkCmd) mountCommands() {
|
||||
atk.addCommand(NewServeCommand(atk))
|
||||
atk.addCommand(NewAdminCommand(atk))
|
||||
atk.addCommand(NewExportCommand(atk))
|
||||
atk.addCommand(NewImportCommand(atk))
|
||||
atk.addCommand(NewConfigCommand())
|
||||
atk.addCommand(NewGenCommand())
|
||||
atk.addCommand(NewUpgradeCommand())
|
||||
atk.addCommand(NewVersionCommand())
|
||||
}
|
||||
|
||||
func (atk *ArtalkCmd) Launch() error {
|
||||
// ===================
|
||||
// 1. Prepare Commands
|
||||
// ===================
|
||||
atk.mountCommands()
|
||||
|
||||
done := make(chan bool, 1) // shutdown signal
|
||||
|
||||
// listen for interrupt signal to gracefully shutdown the application
|
||||
go func() {
|
||||
sigch := make(chan os.Signal, 1)
|
||||
signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)
|
||||
<-sigch
|
||||
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// ===================
|
||||
// 2. Command Execute
|
||||
// ===================
|
||||
go func() {
|
||||
if err := atk.RootCmd.Execute(); err != nil {
|
||||
color.Red(err.Error())
|
||||
}
|
||||
|
||||
done <- true
|
||||
}()
|
||||
|
||||
<-done
|
||||
|
||||
// ===================
|
||||
// 3. App Cleanups
|
||||
// ===================
|
||||
if atk.App != nil {
|
||||
return atk.App.OnTerminate().Trigger(&core.TerminateEvent{
|
||||
App: atk.App,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create new config instance by specific config filename
|
||||
func getConfig(cfgFile string) (*config.Config, error) {
|
||||
// Retrieve config file by default names when specific filename is empty
|
||||
if cfgFile == "" {
|
||||
cfgFile = config.RetrieveConfigFile()
|
||||
}
|
||||
|
||||
// Generate new config file when retrieve failed
|
||||
if cfgFile == "" {
|
||||
cfgFile = config.CONF_DEFAULT_FILENAMES[0]
|
||||
core.Gen("config", cfgFile, false)
|
||||
}
|
||||
|
||||
// Create new config instance and return
|
||||
return config.NewFromFile(cfgFile)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Shortcut Functions
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func flag(cmd *cobra.Command, name string, defaultVal interface{}, usage string) {
|
||||
f := cmd.PersistentFlags()
|
||||
switch y := defaultVal.(type) {
|
||||
case bool:
|
||||
f.Bool(name, y, usage)
|
||||
case int:
|
||||
f.Int(name, y, usage)
|
||||
case string:
|
||||
f.String(name, y, usage)
|
||||
}
|
||||
}
|
||||
|
||||
func flagP(cmd *cobra.Command, name, shorthand string, defaultVal interface{}, usage string) {
|
||||
f := cmd.PersistentFlags()
|
||||
switch y := defaultVal.(type) {
|
||||
case bool:
|
||||
f.BoolP(name, shorthand, y, usage)
|
||||
case int:
|
||||
f.IntP(name, shorthand, y, usage)
|
||||
case string:
|
||||
f.StringP(name, shorthand, y, usage)
|
||||
}
|
||||
}
|
||||
|
||||
func flagV(cmd *cobra.Command, name string, defaultVal interface{}, usage string) {
|
||||
flag(cmd, name, defaultVal, usage)
|
||||
}
|
||||
|
||||
func flagPV(cmd *cobra.Command, name, shorthand string, defaultVal interface{}, usage string) {
|
||||
flagP(cmd, name, shorthand, defaultVal, usage)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewConfigCommand() *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Output Config Information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Get config filename from cmd flags
|
||||
filename, _ := cmd.Flags().GetString("config")
|
||||
|
||||
// Get new config instance
|
||||
config, err := getConfig(filename)
|
||||
if err != nil {
|
||||
log.Fatal("Config fail: ", err)
|
||||
}
|
||||
|
||||
// Output JSON of config
|
||||
buf, _ := json.MarshalIndent(config, "", " ")
|
||||
fmt.Println(string(buf))
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
BootModeKey: MODE_MINI_BOOT,
|
||||
},
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/artransfer"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/ArtalkJS/Artalk/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewExportCommand(app *ArtalkCmd) *cobra.Command {
|
||||
exportCmd := &cobra.Command{
|
||||
Use: "export",
|
||||
Aliases: []string{},
|
||||
Short: "Artransfer export",
|
||||
Long: "\n# Artransfer - Export\n\n See the documentation to learn more: https://artalk.js.org/guide/transfer.html",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
jsonStr, err := artransfer.RunExportArtrans(app.Dao(), &artransfer.ExportParams{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(args) < 1 || args[0] == "" {
|
||||
// write to stdout
|
||||
fmt.Println(jsonStr)
|
||||
} else {
|
||||
filename := args[0]
|
||||
|
||||
// make sure is abs path
|
||||
filename, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// check dir
|
||||
stat, err := os.Stat(filename)
|
||||
if err == nil {
|
||||
if stat.IsDir() {
|
||||
filename = path.Join(filename, "backup-"+time.Now().Format("20060102-150405")+".artrans")
|
||||
}
|
||||
}
|
||||
|
||||
// mkdir -p
|
||||
if err := utils.EnsureDir(filepath.Dir(filename)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// touch
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// >
|
||||
_, err2 := f.WriteString(jsonStr)
|
||||
if err2 != nil {
|
||||
log.Fatal(err2)
|
||||
}
|
||||
|
||||
log.Info(i18n.T("Export complete") + ": " + filename)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return exportCmd
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewGenCommand() *cobra.Command {
|
||||
genCmd := &cobra.Command{
|
||||
Use: "gen <TYPE> <DEST>",
|
||||
Short: "A collection of several useful generators",
|
||||
Long: "Generate some content\ne.g. `artalk gen config ./artalk.yml`",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
specificPath string
|
||||
isForce bool
|
||||
)
|
||||
if len(args) > 1 {
|
||||
specificPath = args[1]
|
||||
}
|
||||
isForce, _ = cmd.Flags().GetBool("force")
|
||||
|
||||
core.Gen(args[0], specificPath, isForce)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
BootModeKey: MODE_MINI_BOOT,
|
||||
},
|
||||
}
|
||||
|
||||
flagPV(genCmd, "force", "f", false, "Force overwrite an existing file")
|
||||
|
||||
return genCmd
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/artransfer"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewImportCommand(app *ArtalkCmd) *cobra.Command {
|
||||
importCmd := &cobra.Command{
|
||||
Use: "import <FILENAME>",
|
||||
Aliases: []string{},
|
||||
Short: "Artransfer import",
|
||||
Long: "\n# Artransfer - Import\n\n See the documentation to learn more: https://artalk.js.org/guide/transfer.html",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
parcelFile := args[0]
|
||||
if _, err := os.Stat(parcelFile); errors.Is(err, os.ErrNotExist) {
|
||||
log.Fatal(i18n.T("{{name}} not found", map[string]interface{}{"name": i18n.T("File")}))
|
||||
}
|
||||
|
||||
payload := args[1:]
|
||||
payload = append(payload, "json_file:"+parcelFile)
|
||||
|
||||
// import Artrans
|
||||
params := artransfer.ArrToImportParams(payload)
|
||||
params.Assumeyes, _ = cmd.Flags().GetBool("assumeyes")
|
||||
artransfer.RunImportArtrans(app.Dao(), params)
|
||||
|
||||
log.Info(i18n.T("Import complete"))
|
||||
},
|
||||
}
|
||||
|
||||
flagPV(importCmd, "assumeyes", "y", false, "Automatically answer yes for all questions.")
|
||||
|
||||
return importCmd
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/ArtalkJS/Artalk/server"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewServeCommand(app *ArtalkCmd) *cobra.Command {
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "Start the server",
|
||||
Long: Banner,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(Banner)
|
||||
fmt.Print("-------------------------------\n\n")
|
||||
|
||||
// init fiber app
|
||||
_, err := server.Serve(app.App)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
flagPV(serverCmd, "host", "", "0.0.0.0", "Listening IP")
|
||||
flagPV(serverCmd, "port", "", 23366, "Listening port")
|
||||
|
||||
return serverCmd
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/config"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/log"
|
||||
"github.com/blang/semver"
|
||||
"github.com/rhysd/go-github-selfupdate/selfupdate"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewUpgradeCommand() *cobra.Command {
|
||||
upgradeCmd := &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Aliases: []string{"update"},
|
||||
Short: "Upgrade to the latest version",
|
||||
Long: "Upgrade Artalk to the latest version, \n update source is GitHub Releases, \n update need to restart Artalk to take effect.",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info(i18n.T("Checking for updates") + "...")
|
||||
|
||||
latest, found, err := selfupdate.DetectLatest("ArtalkJS/Artalk")
|
||||
if err != nil {
|
||||
log.Fatal("Error occurred while detecting version: ", err)
|
||||
}
|
||||
|
||||
ignoreVersionCheck, _ := cmd.Flags().GetBool("force")
|
||||
if !ignoreVersionCheck {
|
||||
v := semver.MustParse(strings.TrimPrefix(config.Version, "v"))
|
||||
if !found || latest.Version.LTE(v) {
|
||||
log.Println(i18n.T("Current version is the latest") + " (v" + v.String() + ")")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Info(i18n.T("New version available") + ": v" + latest.Version.String())
|
||||
log.Info(i18n.T("Downloading") + "...")
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatal("Could not locate executable path ", err)
|
||||
}
|
||||
|
||||
if err := selfupdate.UpdateTo(latest.AssetURL, exe); err != nil {
|
||||
log.Fatal(i18n.T("Update failed")+" ", err)
|
||||
}
|
||||
|
||||
log.Println(i18n.T("Update complete"))
|
||||
fmt.Printf("\n"+
|
||||
"-------------------------------\n"+
|
||||
" v%s Release Note\n"+
|
||||
"-------------------------------\n\n"+
|
||||
"%s\n",
|
||||
latest.Version.String(), latest.ReleaseNotes)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
BootModeKey: MODE_MINI_BOOT,
|
||||
},
|
||||
}
|
||||
|
||||
flagPV(upgradeCmd, "force", "f", false, "Force upgrade ignore version comparison.")
|
||||
|
||||
return upgradeCmd
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
versionCmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Output version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Artalk (" + Version + ")")
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
BootModeKey: MODE_MINI_BOOT,
|
||||
},
|
||||
}
|
||||
|
||||
return versionCmd
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
host: "0.0.0.0"
|
||||
port: 23366
|
||||
app_key: ""
|
||||
debug: false
|
||||
locale: "en"
|
||||
timezone: "Asia/Shanghai"
|
||||
site_default: "Default Site"
|
||||
login_timeout: 259200
|
||||
db:
|
||||
type: "sqlite"
|
||||
file: "./data/artalk.db"
|
||||
table_prefix: ""
|
||||
name: "artalk"
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
user: "root"
|
||||
password: ""
|
||||
charset: "utf8mb4"
|
||||
ssl: false
|
||||
log:
|
||||
enabled: true
|
||||
filename: "./data/artalk.log"
|
||||
cache:
|
||||
enabled: false
|
||||
type: "builtin"
|
||||
expires: 30
|
||||
warm_up: false
|
||||
server: ""
|
||||
redis:
|
||||
network: "tcp"
|
||||
username: ""
|
||||
password: ""
|
||||
db: 0
|
||||
trusted_domains: []
|
||||
ssl:
|
||||
enabled: false
|
||||
cert_path: ""
|
||||
key_path: ""
|
||||
moderator:
|
||||
pending_default: false
|
||||
api_fail_block: false
|
||||
akismet_key: ""
|
||||
tencent:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
aliyun:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
keywords:
|
||||
enabled: false
|
||||
pending: false
|
||||
files:
|
||||
- "./data/keywords_1.txt"
|
||||
file_sep: "\n"
|
||||
replac_to: "x"
|
||||
captcha:
|
||||
enabled: true
|
||||
always: false
|
||||
captcha_type: "image"
|
||||
action_limit: 3
|
||||
action_reset: 60
|
||||
turnstile:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
recaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
hcaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
img_upload:
|
||||
enabled: true
|
||||
path: "./data/artalk-img/"
|
||||
max_size: 5
|
||||
public_path: null
|
||||
upgit:
|
||||
enabled: false
|
||||
exec: "./upgit -c UPGIT_CONF_FILE_PATH -t /artalk-img"
|
||||
del_local: true
|
||||
email:
|
||||
enabled: false
|
||||
send_type: "smtp"
|
||||
send_name: "{{reply_nick}}"
|
||||
send_addr: "noreply@example.com"
|
||||
mail_subject: "[{{site_name}}] You got a reply from @{{reply_nick}}"
|
||||
mail_tpl: "default"
|
||||
smtp:
|
||||
host: "smtp.qq.com"
|
||||
port: 587
|
||||
username: "example@qq.com"
|
||||
password: ""
|
||||
ali_dm:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
account_name: "noreply@example.com"
|
||||
admin_notify:
|
||||
notify_tpl: "default"
|
||||
noise_mode: false
|
||||
email:
|
||||
enabled: true
|
||||
mail_subject: '[{{site_name}}] Post "{{page_title}}" has new a comment'
|
||||
mail_tpl: ""
|
||||
telegram:
|
||||
enabled: false
|
||||
api_token: ""
|
||||
receivers:
|
||||
- 7777777
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
ding_talk:
|
||||
enabled: false
|
||||
token: ""
|
||||
secret: ""
|
||||
slack:
|
||||
enabled: false
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
line:
|
||||
enabled: false
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
frontend:
|
||||
placeholder: ""
|
||||
noComment: ""
|
||||
sendBtn: ""
|
||||
editorTravel: true
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
vote: true
|
||||
voteDown: false
|
||||
uaBadge: false
|
||||
listSort: true
|
||||
pvEl: "#ArtalkPV"
|
||||
countEl: "#ArtalkCount"
|
||||
preview: true
|
||||
flatMode: "auto"
|
||||
nestMax: 2
|
||||
nestSort: DATE_ASC
|
||||
gravatar:
|
||||
mirror: "https://www.gravatar.com/avatar/"
|
||||
params: "d=mp&s=240"
|
||||
pagination:
|
||||
pageSize: 20
|
||||
readMore: true
|
||||
autoLoad: true
|
||||
heightLimit:
|
||||
content: 300
|
||||
children: 400
|
||||
scrollable: false
|
||||
reqTimeout: 15000
|
||||
versionCheck: true
|
||||
@@ -0,0 +1,327 @@
|
||||
# Listen host
|
||||
host: "0.0.0.0"
|
||||
|
||||
# Listen port
|
||||
port: 23366
|
||||
|
||||
# App Key (for generation of JWT)
|
||||
app_key: ""
|
||||
|
||||
# Debug mode
|
||||
debug: false
|
||||
|
||||
# Language (follow Unicode BCP 47) ["en", "zh-CN", "zh-TW", "jp"]
|
||||
# -- see https://www.techonthenet.com/js/language_tags.php --
|
||||
locale: "en"
|
||||
|
||||
# Timezone (follow IANA Time Zone Database)
|
||||
# -- see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones --
|
||||
timezone: "Asia/Shanghai"
|
||||
|
||||
# Default site name (create when app is first launched)
|
||||
site_default: "Default Site"
|
||||
|
||||
# Login timeout (in seconds)
|
||||
login_timeout: 259200
|
||||
|
||||
# Database
|
||||
db:
|
||||
# Database type ["sqlite", "mysql", "pgsql", "mssql"]
|
||||
type: "sqlite"
|
||||
# Database file (only for SQLite)
|
||||
file: "./data/artalk.db"
|
||||
# Table prefix (e.g. "atk_")
|
||||
table_prefix: ""
|
||||
# -- The following is not necessary for SQLite --
|
||||
# Database name
|
||||
name: "artalk"
|
||||
# Host address
|
||||
host: "localhost"
|
||||
# Host port
|
||||
port: 3306
|
||||
# Database user
|
||||
user: "root"
|
||||
# Database password
|
||||
password: ""
|
||||
# Database charset
|
||||
charset: "utf8mb4"
|
||||
# Enable SSL mode
|
||||
ssl: false
|
||||
|
||||
# Logging
|
||||
log:
|
||||
# Enable logging
|
||||
enabled: true
|
||||
# Log file path
|
||||
filename: "./data/artalk.log"
|
||||
|
||||
# Cache
|
||||
cache:
|
||||
# Enable cache
|
||||
enabled: false
|
||||
# Cache type ["redis", "memcache", "builtin"]
|
||||
type: "builtin"
|
||||
# Cache expiration time (in minutes)
|
||||
expires: 30
|
||||
# Cache warm up (warm up cache when program starts)
|
||||
warm_up: false
|
||||
# -- The following is not necessary for `builtin` cache --
|
||||
# Cache server address (e.g. "localhost:6379")
|
||||
server: ""
|
||||
# Redis config
|
||||
redis:
|
||||
# Connection type ["tcp", "unix"]
|
||||
network: "tcp"
|
||||
# Redis username
|
||||
username: ""
|
||||
# Redis password
|
||||
password: ""
|
||||
# Redis database number (e.g. 0)
|
||||
db: 0
|
||||
|
||||
# Trusted domains
|
||||
# -- e.g. ["https://artalk.example.com:23366"] add url of your site her --
|
||||
trusted_domains: []
|
||||
|
||||
# SSL
|
||||
ssl:
|
||||
# Enable SSL
|
||||
enabled: false
|
||||
# Certificate file path
|
||||
# (e.g. "/etc/letsencrypt/live/example.com/fullchain.pem")
|
||||
cert_path: ""
|
||||
# Key file path
|
||||
# (e.g. "/etc/letsencrypt/live/example.com/privkey.pem")
|
||||
key_path: ""
|
||||
|
||||
# Moderator
|
||||
# -- Comment examination before being public --
|
||||
moderator:
|
||||
# Default pending (new comments need to be approved by admin)
|
||||
pending_default: false
|
||||
# Block when API request fails (set to false to let comments pass when API request fails)
|
||||
api_fail_block: false
|
||||
# Akismet Key
|
||||
# (Akismet anti-spam service, https://akismet.com)
|
||||
akismet_key: ""
|
||||
# Tencent Cloud Content Security
|
||||
# (Auto review comments with Tencent Cloud Content Security)
|
||||
# -- see https://cloud.tencent.com/document/product/1124/64508 --
|
||||
tencent:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
# Aliyun Content Security
|
||||
# (Auto review comments with Aliyun Content Security)
|
||||
# -- see https://help.aliyun.com/document_detail/28417.html --
|
||||
aliyun:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
# Keyword filter (local offline dictionary)
|
||||
keywords:
|
||||
# Enable keyword filter
|
||||
enabled: false
|
||||
# Set to pending when match
|
||||
pending: false
|
||||
# Dictionary file (support multiple dictionary files)
|
||||
files:
|
||||
- "./data/keywords_1.txt"
|
||||
file_sep: "\n"
|
||||
replac_to: "x"
|
||||
|
||||
# Captcha
|
||||
captcha:
|
||||
# Enable captcha
|
||||
enabled: true
|
||||
# Captcha is required always
|
||||
always: false
|
||||
# Captcha type ["image", "turnstile", "recaptcha", "hcaptcha", "geetest"]
|
||||
captcha_type: "image"
|
||||
# Action limit
|
||||
# (the number of actions required to activate captcha)
|
||||
action_limit: 3
|
||||
# Reset Timeout (timeout to reset action counter. unit: s, set to -1 to disable)
|
||||
action_reset: 60
|
||||
# Turnstile
|
||||
# (https://www.cloudflare.com/products/turnstile/)
|
||||
turnstile:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# reCaptcha
|
||||
# (https://www.google.com/recaptcha/about/)
|
||||
recaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# hCaptcha (https://www.hcaptcha.com/)
|
||||
hcaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# Geetest (https://www.geetest.com)
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
|
||||
# Upload
|
||||
img_upload:
|
||||
# Enable image upload
|
||||
enabled: true
|
||||
# Image storage
|
||||
path: "./data/artalk-img/"
|
||||
# Image size limit (unit: MB)
|
||||
max_size: 5
|
||||
# Image link base path (default: "/static/images/")
|
||||
public_path: null
|
||||
# Upgit config
|
||||
upgit:
|
||||
# Enable Upgit
|
||||
enabled: false
|
||||
# Command line arguments
|
||||
exec: "./upgit -c UPGIT_CONF_FILE_PATH -t /artalk-img"
|
||||
# Delete local image after upload success
|
||||
del_local: true
|
||||
|
||||
# Email
|
||||
email:
|
||||
# Enable email notification
|
||||
enabled: false
|
||||
# Send method ["smtp", "ali_dm", "sendmail"]
|
||||
send_type: "smtp"
|
||||
# Nick name of sender
|
||||
send_name: "{{reply_nick}}"
|
||||
# Email address of sender
|
||||
send_addr: "noreply@example.com"
|
||||
# Email subject
|
||||
mail_subject: "[{{site_name}}] You got a reply from @{{reply_nick}}"
|
||||
# Email template file (set to file path to use custom template)
|
||||
mail_tpl: "default"
|
||||
# SMTP send (set send method to "smtp" to enable)
|
||||
smtp:
|
||||
# Email address of sender
|
||||
host: "smtp.qq.com"
|
||||
# Email port
|
||||
port: 587
|
||||
# Email address of sender
|
||||
username: "example@qq.com"
|
||||
# Password
|
||||
password: ""
|
||||
# Aliyun mail push
|
||||
# (set send method to "ali_dm" to enable; see: https://help.aliyun.com/document_detail/29444.html)
|
||||
ali_dm:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
account_name: "noreply@example.com"
|
||||
|
||||
# Multi-push
|
||||
admin_notify:
|
||||
# Notification template (set to file path to use custom template)
|
||||
notify_tpl: "default"
|
||||
# Noise mode
|
||||
# -- noise_mode is disabled by default. --
|
||||
# -- When this option is set to `false`, only messages sent to the administrator will be notified, --
|
||||
# -- such as "user A" replies to "user B", the communication between these two users will not be notified to the administrator. --
|
||||
noise_mode: false
|
||||
# Notify admin
|
||||
email:
|
||||
# Enable (can be disabled when using other push methods)
|
||||
enabled: true
|
||||
# Email subject (email subject sent to admin)
|
||||
mail_subject: '[{{site_name}}] Post "{{page_title}}" has new a comment'
|
||||
# Admin email template file (set to file path to use custom template)
|
||||
mail_tpl: ""
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: false
|
||||
api_token: ""
|
||||
receivers:
|
||||
- 7777777
|
||||
# Bark
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
# WebHook
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
ding_talk:
|
||||
enabled: false
|
||||
token: ""
|
||||
secret: ""
|
||||
# Slack
|
||||
slack:
|
||||
enabled: false
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
# LINE
|
||||
line:
|
||||
enabled: false
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
|
||||
# UI Settings
|
||||
frontend:
|
||||
# Comment box placeholder
|
||||
placeholder: ""
|
||||
# Text to display when there is
|
||||
noComment: ""
|
||||
# Text of the send button
|
||||
sendBtn: ""
|
||||
# Movable comment box
|
||||
editorTravel: true
|
||||
# Emoticons
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
# Vote button
|
||||
vote: true
|
||||
# Dislike button
|
||||
voteDown: false
|
||||
# User UA badge
|
||||
uaBadge: false
|
||||
# Comment sorting
|
||||
listSort: true
|
||||
# Page PV binding element
|
||||
pvEl: "#ArtalkPV"
|
||||
# Comment count binding element
|
||||
countEl: "#ArtalkCount"
|
||||
# Editor real-time preview
|
||||
preview: true
|
||||
# Flatten mode ["auto", true, false]
|
||||
flatMode: "auto"
|
||||
# Maximum nesting level
|
||||
nestMax: 2
|
||||
# Nesting comment sorting rules ["DATE_ASC", "DATE_DESC", "VOTE_UP_DESC"]
|
||||
nestSort: DATE_ASC
|
||||
# Gravatar
|
||||
gravatar:
|
||||
# API URL
|
||||
mirror: "https://www.gravatar.com/avatar/"
|
||||
# API parameters
|
||||
params: "d=mp&s=240"
|
||||
# Comment pagination
|
||||
pagination:
|
||||
# Number of comments per page
|
||||
pageSize: 20
|
||||
# Load more mode (disabled to use pagination bar)
|
||||
readMore: true
|
||||
# Scroll loading
|
||||
autoLoad: true
|
||||
# Content height limit
|
||||
heightLimit:
|
||||
# Comment content height limit (unit: px)
|
||||
content: 300
|
||||
# Sub-comment area height limit (unit: px)
|
||||
children: 400
|
||||
# Scrollable (scrollable height limit area)
|
||||
scrollable: false
|
||||
# Request timeout (unit: ms)
|
||||
reqTimeout: 15000
|
||||
# Version check
|
||||
versionCheck: true
|
||||
@@ -0,0 +1,334 @@
|
||||
# 服务器地址
|
||||
host: "0.0.0.0"
|
||||
|
||||
# 服务器端口
|
||||
port: 23366
|
||||
|
||||
# 加密密钥
|
||||
app_key: ""
|
||||
|
||||
# 调试模式
|
||||
debug: false
|
||||
|
||||
# 语言 ["en", "zh-CN", "zh-TW", "jp"]
|
||||
locale: "zh-CN"
|
||||
|
||||
# 时间区域
|
||||
timezone: "Asia/Shanghai"
|
||||
|
||||
# 默认站点名
|
||||
site_default: "默认站点"
|
||||
|
||||
# 登陆有效时长 (单位:秒)
|
||||
login_timeout: 259200
|
||||
|
||||
# 数据库
|
||||
db:
|
||||
# 数据库类型 ["sqlite", "mysql", "pgsql", "mssql"]
|
||||
type: "sqlite"
|
||||
# 数据库文件 (仅 SQLite 数据库需填写)
|
||||
file: "./data/artalk.db"
|
||||
# 数据库名称
|
||||
name: "artalk"
|
||||
# 数据库地址
|
||||
host: "localhost"
|
||||
# 数据库端口
|
||||
port: 3306
|
||||
# 数据库账户
|
||||
user: "root"
|
||||
# 数据库密码
|
||||
password: ""
|
||||
# 编码格式
|
||||
charset: "utf8mb4"
|
||||
# 表前缀 (例如:"atk_")
|
||||
table_prefix: ""
|
||||
# 启用 SSL
|
||||
ssl: false
|
||||
|
||||
# 日志
|
||||
log:
|
||||
# 启用日志
|
||||
enabled: true
|
||||
# 日志文件路径
|
||||
filename: "./data/artalk.log"
|
||||
|
||||
# 缓存
|
||||
cache:
|
||||
# 启用缓存
|
||||
enabled: false
|
||||
# 缓存类型 ["redis", "memcache", "builtin"]
|
||||
type: "builtin"
|
||||
# 缓存过期时间 (单位:分钟)
|
||||
expires: 30
|
||||
# 缓存启动预热 (程序启动时预热缓存)
|
||||
warm_up: false
|
||||
# 缓存服务器地址 (例如:"localhost:6379")
|
||||
server: ""
|
||||
# Redis 配置
|
||||
redis:
|
||||
# 连接方式 ["tcp", "unix"]
|
||||
network: "tcp"
|
||||
# 用户名
|
||||
username: ""
|
||||
# 密码
|
||||
password: ""
|
||||
# 数据库编号 (例如使用零号数据库填写 0)
|
||||
db: 0
|
||||
|
||||
# 可信域名
|
||||
trusted_domains: [] # 例如:["https://artalk.example.com:23366"]
|
||||
|
||||
# SSL
|
||||
ssl:
|
||||
# 启用 SSL
|
||||
enabled: false
|
||||
# 证书文件路径
|
||||
cert_path: ""
|
||||
# 密钥文件路径
|
||||
key_path: ""
|
||||
|
||||
# 管理员账户
|
||||
admin_users:
|
||||
# - name: "admin"
|
||||
# email: "admin@example.com"
|
||||
# password: "" # 支持 bcrypt 或 md5 加密,如:"(md5)50c21190c6e4e5418c6a90d2b5031119"
|
||||
# badge_name: "管理员"
|
||||
# badge_color: "#0083FF"
|
||||
|
||||
# 评论审核
|
||||
moderator:
|
||||
# 默认待审 (发表新评论需要后台人工审核后才能显示)
|
||||
pending_default: false
|
||||
# API 请求错误时拦截 (关闭此项当请求错误时让评论放行)
|
||||
api_fail_block: false
|
||||
# Akismet Key
|
||||
# (Akismet 反垃圾服务,https://akismet.com)
|
||||
akismet_key: ""
|
||||
# 腾讯云文本内容安全
|
||||
# (https://cloud.tencent.com/document/product/1124/64508)
|
||||
tencent:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
# 阿里云内容安全
|
||||
# (https://help.aliyun.com/document_detail/28417.html)
|
||||
aliyun:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
# 关键词过滤 (本地离线词库)
|
||||
keywords:
|
||||
enabled: false
|
||||
# 匹配成功设为待审状态
|
||||
pending: false
|
||||
# 词库文件 (支持多个词库文件)
|
||||
files:
|
||||
- "./data/词库_1.txt"
|
||||
# 词库文件内容分割符 (例如填写 "\n" 文件中一行一个关键词)
|
||||
file_sep: "\n"
|
||||
# 替换字符
|
||||
replac_to: "x"
|
||||
|
||||
# 验证码
|
||||
captcha:
|
||||
# 启用验证码
|
||||
enabled: true
|
||||
# 总是需要验证码
|
||||
always: false
|
||||
# 验证类型 ["image", "turnstile", "recaptcha", "hcaptcha", "geetest"]
|
||||
captcha_type: "image"
|
||||
# 激活验证码所需操作次数
|
||||
action_limit: 3
|
||||
# 重置操作计数器超时 (单位:s, 设为 -1 不重置)
|
||||
action_reset: 60
|
||||
# Turnstile
|
||||
# (https://www.cloudflare.com/products/turnstile/)
|
||||
turnstile:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# reCaptcha
|
||||
# (https://www.google.com/recaptcha/about/)
|
||||
recaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# hCaptcha (https://www.hcaptcha.com/)
|
||||
hcaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# Geetest 极验 (https://www.geetest.com)
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
|
||||
# IP 归属地
|
||||
ip_region:
|
||||
# 启用 IP 归属地展示
|
||||
enabled: false
|
||||
# 数据文件路径 (.xdb 格式)
|
||||
db_path: "./data/ip2region.xdb"
|
||||
# 显示精度 ["province", "city", "country"]
|
||||
precision: "province"
|
||||
|
||||
# 图片上传
|
||||
img_upload:
|
||||
# 启用图片上传
|
||||
enabled: true
|
||||
# 图片存放路径
|
||||
path: "./data/artalk-img/"
|
||||
# 图片大小限制 (单位:MB)
|
||||
max_size: 5
|
||||
# 图片链接基础路径 (默认为 "/static/images/")
|
||||
public_path: null
|
||||
# Upgit 配置
|
||||
# (使用 Upgit 将图片上传到 GitHub 或图床:https://github.com/pluveto/upgit)
|
||||
upgit:
|
||||
# 启用 Upgit
|
||||
enabled: false
|
||||
# 命令行参数
|
||||
exec: "./upgit -c <upgit配置文件路径> -t /artalk-img"
|
||||
# 上传后删除本地的图片
|
||||
del_local: true
|
||||
|
||||
# 邮件通知
|
||||
email:
|
||||
# 启用邮件通知
|
||||
enabled: false
|
||||
# 发送方式 ["smtp", "ali_dm", "sendmail"]
|
||||
send_type: "smtp"
|
||||
# 发信人昵称
|
||||
send_name: "{{reply_nick}}"
|
||||
# 发信人地址
|
||||
send_addr: "noreply@example.com"
|
||||
# 邮件标题
|
||||
mail_subject: "[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复"
|
||||
# 邮件模板文件 (填入文件路径使用自定义模板)
|
||||
mail_tpl: "default"
|
||||
# SMTP 发送 (启用请将发送方式设为 "smtp")
|
||||
smtp:
|
||||
# 发件地址
|
||||
host: "smtp.qq.com"
|
||||
# 发件端口
|
||||
port: 587
|
||||
# 用户名
|
||||
username: "example@qq.com"
|
||||
# 密码
|
||||
password: ""
|
||||
# 阿里云邮件推送
|
||||
# (启用请将发送方式设为 "ali_dm";参考:https://help.aliyun.com/document_detail/29444.html)
|
||||
ali_dm:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
account_name: "noreply@example.com"
|
||||
|
||||
# 多元推送
|
||||
admin_notify:
|
||||
# 通知模版 (填入文件路径使用自定义模板)
|
||||
notify_tpl: "default"
|
||||
# 嘈杂模式
|
||||
noise_mode: false
|
||||
# 邮件通知管理员
|
||||
email:
|
||||
# 开启 (当使用其他推送方式时,可以关闭管理员邮件通知)
|
||||
enabled: true
|
||||
# 邮件标题 (发送给管理员的邮件标题)
|
||||
mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复"
|
||||
# 管理员邮件模板文件 (填入文件路径使用自定义模板)
|
||||
mail_tpl: ""
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: false
|
||||
api_token: ""
|
||||
receivers:
|
||||
- 7777777
|
||||
# Bark
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
# 飞书
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
# WebHook
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
# 钉钉
|
||||
ding_talk:
|
||||
enabled: false
|
||||
token: ""
|
||||
secret: ""
|
||||
# Slack
|
||||
slack:
|
||||
enabled: false
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
# LINE
|
||||
line:
|
||||
enabled: false
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
|
||||
# 前端配置
|
||||
frontend:
|
||||
# 评论框占位文字
|
||||
placeholder: ""
|
||||
# 无评论显示文字
|
||||
noComment: ""
|
||||
# 发送按钮文字
|
||||
sendBtn: ""
|
||||
# 评论框穿梭
|
||||
editorTravel: true
|
||||
# 表情包
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
# 投票按钮
|
||||
vote: true
|
||||
# 反对按钮
|
||||
voteDown: false
|
||||
# 用户 UA 徽标
|
||||
uaBadge: false
|
||||
# 评论排序功能
|
||||
listSort: true
|
||||
# 页面 PV 绑定元素
|
||||
pvEl: "#ArtalkPV"
|
||||
# 评论数绑定元素
|
||||
countEl: "#ArtalkCount"
|
||||
# 编辑器实时预览功能
|
||||
preview: true
|
||||
# 平铺模式 ["auto", true, false]
|
||||
flatMode: "auto"
|
||||
# 最大嵌套层数
|
||||
nestMax: 2
|
||||
# 嵌套评论排序规则 ["DATE_ASC", "DATE_DESC", "VOTE_UP_DESC"]
|
||||
nestSort: DATE_ASC
|
||||
# 头像 Gravatar
|
||||
gravatar:
|
||||
# API 地址
|
||||
mirror: "https://cravatar.cn/avatar/"
|
||||
# API 参数
|
||||
params: "d=mp&s=240"
|
||||
# 评论分页
|
||||
pagination:
|
||||
# 每页评论数
|
||||
pageSize: 20
|
||||
# 加载更多模式 (关闭则使用分页条)
|
||||
readMore: true
|
||||
# 滚动加载
|
||||
autoLoad: true
|
||||
# 内容限高
|
||||
heightLimit:
|
||||
# 评论内容限高 (单位:px)
|
||||
content: 300
|
||||
# 子评论区域限高 (单位:px)
|
||||
children: 400
|
||||
# 滚动限高 (允许限高区域滚动)
|
||||
scrollable: false
|
||||
# 请求超时 (单位:毫秒)
|
||||
reqTimeout: 15000
|
||||
# 版本检测
|
||||
versionCheck: true
|
||||
@@ -0,0 +1,12 @@
|
||||
version: "3.5"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ "$1" != 'gen' ] && ( [ ! -e /data/artalk.yml ] && [ ! -e /data/artalk-go.yml ] ); then
|
||||
if [ -e /conf.yml ]; then
|
||||
# Move original config to `/data/` for upgrade (<= v2.1.8)
|
||||
cp /conf.yml /data/artalk.yml
|
||||
upMsg=""
|
||||
upMsg+=$'# [v2.1.9+ Updated]\n'
|
||||
upMsg+=$'# The new version of the Artalk container recommends mounting\n'
|
||||
upMsg+=$'# an entire folder instead of a single file to avoid some issues.\n'
|
||||
upMsg+=$'#\n'
|
||||
upMsg+=$'# The original config file has been moved to the "/data/" folder,\n'
|
||||
upMsg+=$'# please unmount the config file volume from your container\n'
|
||||
upMsg+=$'# and edit "/data/artalk.yml" for configuration.'
|
||||
echo "$upMsg" > /conf.yml
|
||||
echo "$(date) [info] Copy config file from '/conf.yml' to '/data/artalk.yml' for upgrade"
|
||||
else
|
||||
# Generate new config
|
||||
artalk gen conf /data/artalk.yml
|
||||
echo "$(date) [info] Generate new config file to '/data/artalk.yml'"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run Artalk
|
||||
/usr/bin/artalk "$@"
|
||||
@@ -0,0 +1,3 @@
|
||||
/.vitepress/dist/
|
||||
/.vitepress/cache/
|
||||
/public/http-api.html
|
||||
@@ -0,0 +1,2 @@
|
||||
shamefully-hoist=true
|
||||
auto-install-peers=true
|
||||
@@ -0,0 +1,211 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import iterator from 'markdown-it-for-inline'
|
||||
import * as Version from '../code/ArtalkVersion.json'
|
||||
|
||||
export default defineConfig({
|
||||
title: 'Artalk',
|
||||
description: '一款简洁的自托管评论系统',
|
||||
lang: 'zh-CN',
|
||||
|
||||
head: [
|
||||
['link', { rel: 'icon', href: '/favicon.png' }],
|
||||
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi' }],
|
||||
// artalk
|
||||
// ['link', { href: `https://npm.elemecdn.com/artalk@${Version.latest}/dist/Artalk.css`, rel: 'stylesheet' }],
|
||||
['link', { href: `https://artalk.qwqaq.com/dist/Artalk.css`, rel: 'stylesheet' }],
|
||||
// ['script', { src: ArtalkCDN.JS }],
|
||||
// light gallery
|
||||
['link', { href: 'https://npm.elemecdn.com/lightgallery@2.3.0/css/lightgallery.css', rel: 'stylesheet' }],
|
||||
['script', { src: 'https://npm.elemecdn.com/lightgallery@2.3.0/lightgallery.min.js' }],
|
||||
// katex
|
||||
// ['link', { href: "https://npm.elemecdn.com/katex@0.15.3/dist/katex.min.css", rel: 'stylesheet' }],
|
||||
// ['script', { src: 'https://npm.elemecdn.com/katex@0.15.3/dist/katex.min.js' }],
|
||||
// ['script', { src: 'https://npm.elemecdn.com/@artalkjs/plugin-katex/dist/artalk-plugin-katex.js' }],
|
||||
],
|
||||
|
||||
lastUpdated: true,
|
||||
|
||||
markdown: {
|
||||
// @link https://github.com/shikijs/shiki
|
||||
theme: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
},
|
||||
config: (md) => {
|
||||
md.use(iterator, 'artalk_version', 'text', function (tokens, idx) {
|
||||
tokens[idx].content = tokens[idx].content.replace(/:ArtalkVersion:/g, Version.latest)
|
||||
});
|
||||
md.use(iterator, 'artalk_version_link', 'link_open', (tokens, idx) => {
|
||||
const href = tokens[idx].attrGet('href')
|
||||
tokens[idx].attrSet('href', href.replace(/:ArtalkVersion:/g, Version.latest))
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
locales: {
|
||||
root: {
|
||||
label: '简体中文',
|
||||
lang: 'zh'
|
||||
},
|
||||
en: {
|
||||
label: 'English',
|
||||
lang: 'en',
|
||||
link: 'https://artalk-js-org.translate.goog/guide/intro?_x_tr_sl=auto&_x_tr_tl=en-US&_x_tr_hl=en-US&_x_tr_pto=wapp'
|
||||
// link: '/en/'
|
||||
}
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
sidebar: {
|
||||
"/guide/": [
|
||||
{
|
||||
text: "快速开始",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '项目介绍', link: '/guide/intro.md' },
|
||||
{ text: '程序部署', link: '/guide/deploy.md' },
|
||||
{ text: '数据迁移', link: '/guide/transfer.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "核心指南",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '侧边栏', link: '/guide/frontend/sidebar.md' },
|
||||
{ text: '邮件通知', link: '/guide/backend/email.md' },
|
||||
{ text: '多元推送', link: '/guide/backend/admin_notify.md' },
|
||||
{ text: '评论审核', link: '/guide/backend/moderator.md' },
|
||||
{ text: '验证码', link: '/guide/backend/captcha.md' },
|
||||
{ text: '图片上传', link: '/guide/backend/img-upload.md' },
|
||||
{ text: '账户与多站点', link: '/guide/backend/multi-site.md' },
|
||||
{ text: '解析相对路径', link: '/guide/backend/relative-path.md' },
|
||||
{ text: '如何配置界面', link: '/guide/backend/fe-control.md' },
|
||||
{ text: 'English (i18n)', link: '/guide/frontend/i18n.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "进阶指南",
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '表情包', link: '/guide/frontend/emoticons.md' },
|
||||
{ text: '浏览量统计', link: '/guide/frontend/pv.md' },
|
||||
{ text: 'Latex', link: '/guide/frontend/latex.md' },
|
||||
{ text: '图片灯箱', link: '/guide/frontend/lightbox.md' },
|
||||
{ text: 'IP 归属地', link: '/guide/frontend/ip-region.md' },
|
||||
{ text: '精简版本', link: '/guide/frontend/artalk-lite.md' },
|
||||
{ text: '置入博客', link: '/guide/frontend/import-blog.md' },
|
||||
{ text: '置入框架', link: '/guide/frontend/import-framework.md' },
|
||||
{ text: '前端 API', link: '/develop/fe-api.md', },
|
||||
{ text: '开发文档', link: '/develop/index.md', },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "配置文档",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '后端配置', link: '/guide/backend/config.md' },
|
||||
{ text: '前端配置', link: '/guide/frontend/config.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "部署说明",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '守护进程', link: '/guide/backend/daemon.md' },
|
||||
{ text: '反向代理', link: '/guide/backend/reverse-proxy.md' },
|
||||
{ text: '编译构建', link: '/guide/backend/build.md' },
|
||||
{ text: '程序升级', link: '/guide/backend/update.md' },
|
||||
{ text: 'Docker', link: '/guide/backend/docker.md' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '更多内容',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '安全防范', link: '/guide/security.md' },
|
||||
{ text: '扩展阅读', link: '/guide/extras.md' },
|
||||
{ text: '案例展示', link: '/guide/cases.md' },
|
||||
{ text: '关于我们', link: '/guide/about.md' },
|
||||
]
|
||||
}
|
||||
],
|
||||
"/develop/": [
|
||||
{
|
||||
text: '开发文档',
|
||||
items: [
|
||||
{ text: '开发说明', link: '/develop/index.md', },
|
||||
{ text: '前端 API', link: '/develop/fe-api.md', },
|
||||
{ text: '前端 Event', link: '/develop/event.md', },
|
||||
{ text: '插件开发', link: '/develop/plugs.md' },
|
||||
{ text: '🔬 HTTP API', link: 'https://artalk.js.org/http-api.html', },
|
||||
{ text: '📖 Contributing Guide', link: 'https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md' },
|
||||
{ text: '🔖 Code of Conduct', link: 'https://github.com/ArtalkJS/Artalk/blob/master/CODE_OF_CONDUCT.md' },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
nav: [
|
||||
// NavbarItem
|
||||
{
|
||||
text: '介绍',
|
||||
link: '/guide/intro',
|
||||
},
|
||||
{
|
||||
text: '部署',
|
||||
link: '/guide/deploy',
|
||||
},
|
||||
{
|
||||
text: '配置',
|
||||
link: '/guide/backend/config',
|
||||
},
|
||||
{
|
||||
text: '迁移',
|
||||
link: '/guide/transfer',
|
||||
},
|
||||
{
|
||||
text: '案例',
|
||||
link: '/guide/cases',
|
||||
},
|
||||
{
|
||||
text: '开发',
|
||||
link: '/develop/',
|
||||
},
|
||||
// NavbarGroup
|
||||
{
|
||||
text: '传送',
|
||||
items: [
|
||||
{
|
||||
text: '代码仓库',
|
||||
link: 'https://github.com/ArtalkJS/Artalk',
|
||||
},
|
||||
{
|
||||
text: '文档镜像 (国内)',
|
||||
link: 'https://artalk-docs.qwqaq.com'
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/ArtalkJS/Artalk' }
|
||||
],
|
||||
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '2WNJ32WVTY',
|
||||
apiKey: '6c6ebc345a87b738264f19095b78c91c',
|
||||
indexName: 'artalk-js',
|
||||
searchParameters: {
|
||||
facetFilters: ['lang:zh-CN']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/ArtalkJS/Artalk/edit/master/docs/:path',
|
||||
text: '完善文档'
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div ref="el" style="margin-top: 20px;"></div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, nextTick, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
import type Artalk from 'artalk'
|
||||
|
||||
const el = ref<HTMLElement | null>(null)
|
||||
|
||||
const router = useRouter()
|
||||
const page = useData().page
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
initArtalk(getConfByPage())
|
||||
})
|
||||
|
||||
watch(() => router.route.data.relativePath, () => {
|
||||
nextTick(() => {
|
||||
artalk.update(getConfByPage())
|
||||
artalk.reload()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
|
||||
function initArtalk(conf: any) {
|
||||
import('artalk').then(({ default: Artalk }) => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
gravatar: {
|
||||
mirror: 'https://cravatar.cn/avatar/'
|
||||
},
|
||||
...conf
|
||||
})
|
||||
}).then(() => {
|
||||
loadExtraFuncs()
|
||||
})
|
||||
}
|
||||
|
||||
function getConfByPage() {
|
||||
return {
|
||||
pageKey: 'https://artalk.js.org'+location.pathname,
|
||||
pageTitle: page.value.title,
|
||||
server: 'https://artalk.qwqaq.com',
|
||||
site: 'ArtalkDocs',
|
||||
}
|
||||
}
|
||||
|
||||
function loadExtraFuncs() {
|
||||
// 图片灯箱插件
|
||||
artalk.on('list-loaded', () => {
|
||||
document.querySelectorAll('.atk-comment .atk-content').forEach(($content) => {
|
||||
const imgEls = $content.querySelectorAll<HTMLImageElement>('img:not([atk-emoticon]):not([atk-lightbox])');
|
||||
imgEls.forEach((imgEl) => {
|
||||
imgEl.setAttribute('atk-lightbox', '')
|
||||
const linkEl = document.createElement('a')
|
||||
linkEl.setAttribute('class', 'atk-img-link')
|
||||
linkEl.setAttribute('href', imgEl.src)
|
||||
linkEl.setAttribute('data-src', imgEl.src)
|
||||
linkEl.append(imgEl.cloneNode())
|
||||
imgEl.replaceWith(linkEl)
|
||||
})
|
||||
// @ts-ignore
|
||||
if (imgEls.length) lightGallery($content, { selector: '.atk-img-link' })
|
||||
})
|
||||
})
|
||||
|
||||
// 夜间模式
|
||||
const darkMode = document.querySelector('html').classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
|
||||
new MutationObserver((mList) => {
|
||||
mList.forEach((m) => {
|
||||
if (m.attributeName !== 'class') return
|
||||
|
||||
// @ts-ignore
|
||||
const darkMode = m.target.classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html'), { attributes: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<iframe src="https://artransfer.netlify.app/?iframe=1" style="width: 100%;height: 520px;border: 0;" id="artransferIframe"></iframe>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
|
||||
function setArtransferIframeDarkMode(value: boolean) {
|
||||
const iframe = document.querySelector<HTMLIFrameElement>('#artransferIframe')!
|
||||
if (value) {
|
||||
iframe.src = iframe.src + '&dark=1'
|
||||
} else {
|
||||
iframe.src = iframe.src.replace(/&dark=1$/, '')
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
mounted: () => {
|
||||
const darkMode = document.querySelector('html').classList.contains('dark')
|
||||
setArtransferIframeDarkMode(darkMode)
|
||||
|
||||
// dark_mode 监听
|
||||
new MutationObserver((mList) => {
|
||||
mList.forEach((m) => {
|
||||
if (m.attributeName !== 'class') return
|
||||
|
||||
// @ts-ignore
|
||||
const darkMode = m.target.classList.contains('dark')
|
||||
setArtransferIframeDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html'), { attributes: true })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup>
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { onMounted } from 'vue'
|
||||
import './style.scss'
|
||||
|
||||
const { Layout } = DefaultTheme
|
||||
|
||||
onMounted(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #doc-after>
|
||||
<Artalk />
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
declare const Artalk: any
|
||||
@@ -0,0 +1,20 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import Layout from './Layout.vue'
|
||||
import Artalk from './Artalk.vue'
|
||||
import Artransfer from './Artransfer.vue'
|
||||
import { Theme } from 'vitepress'
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
|
||||
Layout,
|
||||
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
app.component('Artransfer', Artransfer)
|
||||
app.component('Artalk', Artalk)
|
||||
|
||||
// app is the Vue 3 app instance from `createApp()`.
|
||||
// router is VitePress' custom router. `siteData` is
|
||||
// a `ref` of current site-level metadata.
|
||||
}
|
||||
} as Theme
|
||||
@@ -0,0 +1,150 @@
|
||||
:root {
|
||||
--vp-c-brand: #558fb5;
|
||||
--vp-c-brand-light: #498cb8;
|
||||
--vp-c-brand-lighter: #549ccc;
|
||||
--vp-c-brand-dark: #366482;
|
||||
--vp-c-brand-darker: #244f6b;
|
||||
--vp-code-block-bg: #f6f8fa;
|
||||
--vp-c-divider: #dfe2e5;
|
||||
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: linear-gradient(90deg,#0083ff, #37dfd9);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-bg: #22272e;
|
||||
--vp-c-bg-alt: #22272e;
|
||||
--vp-code-block-bg: #2b313a;
|
||||
--vp-c-text-2: #8094a8;
|
||||
--vp-button-alt-bg: #1e2224;
|
||||
--vp-button-alt-border: #2d3235;
|
||||
--vp-c-bg-soft: #1e2224;
|
||||
--vp-c-divider-light: #2d3235;
|
||||
--vp-c-divider: #34404c;
|
||||
--vp-c-black: #22272e;
|
||||
--vp-c-bg-soft: #2b313a;
|
||||
}
|
||||
|
||||
.VPHome .clip {
|
||||
font-weight: 500;
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.VPHome .VPButton.brand {
|
||||
border-color: #0083ff;
|
||||
color: #fff;
|
||||
background-color: #0083ff;
|
||||
|
||||
&:hover {
|
||||
border-color: #007CF0;
|
||||
color: #fff;
|
||||
background-color: #007CF0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 一起摇摆 **/
|
||||
.wave {
|
||||
display: inline-block;
|
||||
transform-origin: center bottom;
|
||||
animation:upAnimation 2.33s ease .8s 3 both;
|
||||
}
|
||||
@keyframes upAnimation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
}
|
||||
10% {
|
||||
transform: rotate(-12deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(12deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
}
|
||||
28% {
|
||||
transform: rotate(-10deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
}
|
||||
36% {
|
||||
transform: rotate(10deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
42% {
|
||||
transform: rotate(-8deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
48% {
|
||||
transform: rotate(8deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
52% {
|
||||
transform: rotate(-4deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
56% {
|
||||
transform: rotate(4deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
60% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.vp-doc > div > img {
|
||||
max-width: 100%;
|
||||
margin: 1.5em auto;
|
||||
}
|
||||
|
||||
img[atk-emoticon] {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
/* 由于 body 未添加一致的 transition,导致其他地方暗黑模式切换加 transition 比较怪 */
|
||||
.vp-doc div[class*='language-'] { transition: none; }
|
||||
.vp-doc :not(pre) > code { transition: none; }
|
||||
|
||||
.vp-doc {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
display: block;
|
||||
overflow-x: auto
|
||||
}
|
||||
|
||||
td,th {
|
||||
padding: .6em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 */
|
||||
.VPSidebar::-webkit-scrollbar {
|
||||
width: 7px
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-track {
|
||||
background-color: transparent
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-thumb {
|
||||
background-color: var(--vp-c-divider)
|
||||
}
|
||||
|
||||
// details block patch
|
||||
.custom-block.details > summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vp-code-group .tabs > * {
|
||||
color: var(--vp-c-text-1) !important;
|
||||
}
|
||||
|
||||
.vp-code-group .tabs::after {
|
||||
background-color: var(--vp-c-divider)
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import { defineComponent } from 'vue';
|
||||
const component: ReturnType<typeof defineComponent>;
|
||||
export default component;
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,19 @@
|
||||
## Artalk Documentation
|
||||
|
||||
> The Documentation of Artalk
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
pnpm docs:dev
|
||||
pnpm docs:build
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
[](https://app.netlify.com/sites/artalk/deploys)
|
||||
|
||||
文档通过 [Netlify](https://www.netlify.com/) 对 master 分支进行自动部署。
|
||||
|
||||
## Licensed
|
||||
CC BY-NC-SA 4.0
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"latest": "2.7.1"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<!-- CSS -->
|
||||
<link href="https://unpkg.com/artalk@2.4.4/dist/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="https://unpkg.com/artalk@2.4.4/dist/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments', // 绑定元素的 Selector
|
||||
pageKey: '/post/1', // 固定链接 (留空自动获取)
|
||||
pageTitle: '关于引入 Artalk 这档子事', // 页面标题 (留空自动获取)
|
||||
server: 'http://localhost:8080', // 后端地址
|
||||
site: 'Artalk 的博客', // 你的站点名
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
$ tar xvzf artalk.tar.gz
|
||||
$ cd artalk
|
||||
$ vim artalk.yml
|
||||
$ ./artalk server
|
||||
@@ -0,0 +1,592 @@
|
||||
# HTTP API
|
||||
|
||||
::: warning
|
||||
该 HTTP API 文档已过时
|
||||
:::
|
||||
|
||||
::: tip 无特殊说明时 API 调用响应格式
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| ----- | ------- | --- |
|
||||
| `success` | Boolean | 接口调用结果 |
|
||||
| `msg` | String | 返回消息,出错时此字段包含错误消息 |
|
||||
| `data` | Any | 返回数据,可能为 Object / String / Array 类型 |
|
||||
|
||||
:::
|
||||
|
||||
## Normal API
|
||||
|
||||
### 评论新增
|
||||
|
||||
**POST** `/api/add`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `name` | String | 是 | 待新增评论者昵称 |
|
||||
| `email` | String | 是 | 待新增评论者邮箱 |
|
||||
| `link` | String | 否 | 待新增评论者链接 |
|
||||
| `content` | String | 是 | 待新增评论内容 |
|
||||
| `rid` | Number | 否 | 待新增评论为回复评论时传入父评论 `ID`,否则为 `0` |
|
||||
| `page_key` | String | 是 | 待新增评论目标页面唯一标识符 |
|
||||
| `page_title` | String | 否 | 待新增评论目标页面标题 |
|
||||
| `token` | String | 否 | 评论请求 Token |
|
||||
| `site_name` | String | 否 | 待新增评论目标站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 评论新增成功 |
|
||||
| `data.comment` | Object | - | 评论数据 |
|
||||
| `data.comment.id` | Number | - | 评论 ID |
|
||||
| `data.comment.content` | String | - | 评论正文 |
|
||||
| `data.comment.nick` | String | - | 评论者昵称 |
|
||||
| `data.comment.email_encrypted` | String | - | 评论者邮箱 MD5 加密值 |
|
||||
| `data.comment.link` | String | - | 评论者链接 |
|
||||
| `data.comment.ua` | String | - | 评论者 User-Agent |
|
||||
| `data.comment.date` | String | - | 评论时间,格式为 `1970-01-01 00:00:00` |
|
||||
| `data.comment.is_collapsed` | Boolean | - | 评论是否折叠 |
|
||||
| `data.comment.is_pending` | Boolean | - | 评论是否待审 |
|
||||
| `data.comment.is_allow_reply` | Boolean | - | 评论是否允许回复 |
|
||||
| `data.comment.rid` | Number | `0` | 待新增评论为回复评论时返回父评论 `ID` |
|
||||
| `data.comment.badge_name` | String | - | 评论者徽章文字 |
|
||||
| `data.comment.badge_color` | String | - | 评论者徽章颜色 |
|
||||
| `data.comment.visible` | Boolean | `true` | 评论是否可见 |
|
||||
| `data.comment.vote_up` | Number | `0` | 评论赞同数 |
|
||||
| `data.comment.vote_down` | Number | `0` | 评论反对数 |
|
||||
| `data.comment.page_key` | String | - | 评论所在页面唯一标识符 |
|
||||
| `data.comment.site_name` | String | - | 评论所在站点名称 |
|
||||
|
||||
### 评论获取
|
||||
|
||||
**POST** `/api/get`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `page_key` | String | 是 | 待获取评论页面的唯一标识符 |
|
||||
| `limit` | Number | 否 | 待获取评论的数量限制 |
|
||||
| `offset` | Number | 否 | 待获取评论的起始位置偏移 |
|
||||
| `type` | String | 否 | 获取指定类型的评论 |
|
||||
| `name` | String | 否 | 获取指定昵称的评论 |
|
||||
| `email` | String | 否 | 获取指定邮箱的评论 |
|
||||
| `site_name` | String | 否 | 获取指定站点名称的评论 |
|
||||
| `flat_mode` | Boolean | 否 | 待获取评论是否平铺模式 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 评论获取成功 |
|
||||
| `data.comments` | Array | - | 页面评论数据 |
|
||||
| `data.total` | Number | - | 页面评论总数(包括所有子评论) |
|
||||
| `data.total_parents` | Number | - | 页面评论中父级评论总数 |
|
||||
| `data.page` | Object | - | 页面数据 |
|
||||
| `data.page.id` | Number | - | 页面 ID |
|
||||
| `data.page.admin_only` | Boolean | - | 页面是否仅管理员可评论 |
|
||||
| `data.page.key` | String | - | 页面唯一标识符 |
|
||||
| `data.page.url` | String | - | 页面链接 |
|
||||
| `data.page.title` | String | - | 页面标题 |
|
||||
| `data.page.site_name` | String | - | 页面所在站点名称 |
|
||||
| `data.page.vote_up` | Number | - | 页面赞同数 |
|
||||
| `data.page.vote_down` | Number | - | 页面反对数 |
|
||||
| `data.unread` | Array | - | 未读的评论提醒数据 |
|
||||
| `data.unread_count` | Number | - | 未读的评论提醒数据总数 |
|
||||
| `data.api_version` | Object | - | 目标 Artalk 后端版本数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.comments` 数组中元素结构参考 [评论新增接口](#评论新增) `/api/add` 响应的 `data.comment`
|
||||
- `data.unread` 数组中元素结构如下
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `id` | Number | - | 评论提醒 ID |
|
||||
| `user_id` | Number | - | 评论提醒目标用户 ID |
|
||||
| `comment_id` | Number | - | 评论提醒来源评论 ID |
|
||||
| `is_read` | Boolean | - | 评论提醒是否已读 |
|
||||
| `is_emailed` | Boolean | - | 评论提醒是否已发送邮件 |
|
||||
| `read_link` | String | - | 评论提醒已读地址 |
|
||||
- `data.api_version` 对象结构参考 [Artalk 版本接口](#artalk-版本) `/api/version` 响应
|
||||
|
||||
:::
|
||||
|
||||
### 用户获取
|
||||
|
||||
**POST** `/api/user-get`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `name` | String | 否 | 待获取用户的昵称 |
|
||||
| `email` | String | 否 | 待获取用户的邮箱 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 用户获取成功 |
|
||||
| `data.user` | Object | `null` | 获取的用户数据,若用户不存在返回空 |
|
||||
| `data.user.id` | Number | - | 用户 ID |
|
||||
| `data.user.name` | String | - | 用户昵称 |
|
||||
| `data.user.email` | String | - | 用户邮箱 |
|
||||
| `data.user.link` | String | - | 用户链接 |
|
||||
| `data.user.badge_name` | String | - | 用户徽章文字 |
|
||||
| `data.user.badge_color` | String | - | 用户徽章颜色 |
|
||||
| `data.user.is_admin` | Boolean | - | 用户是否管理员 |
|
||||
| `data.is_login` | Boolean | - | 用户是否登录 |
|
||||
| `data.unread` | Array | `[]` | 用户未读的评论提醒数据 |
|
||||
| `data.unread_count` | Number | `0` | 用户未读的评论提醒数据总数 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.unread` 数组中元素结构参考 [评论获取接口](#评论获取) `/api/get` **TIP**
|
||||
|
||||
:::
|
||||
|
||||
### 用户登录
|
||||
|
||||
**GET/POST** `/api/login`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `name` | String | 是 | 待登录用户的昵称 |
|
||||
| `email` | String | 是 | 待登录用户的邮箱 |
|
||||
| `password` | String | 是 | 待登录用户的密码(管理员身份验证) |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 用户登录成功 |
|
||||
| `data.token` | String | - | 登录的用户 Token |
|
||||
|
||||
### 提醒已读
|
||||
|
||||
**POST** `/api/mark-read`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `notify_key` | String | 否 | 评论提醒唯一标识符 |
|
||||
| `name` | String | 否 | 评论提醒用户的昵称 |
|
||||
| `email` | String | 否 | 评论提醒用户的邮箱 |
|
||||
| `all_read` | Boolean | 否 | 评论提醒是否已读 |
|
||||
| `site_name` | String | 否 | 评论提醒所在站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 提醒已读成功 |
|
||||
|
||||
### 评论投票
|
||||
|
||||
**POST** `/api/vote`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `target_id` | Number | 是 | 待投票评论 ID |
|
||||
| `type` | String | 否 | 待投票类型,前缀 `comment_`、`page_` 依次代表对评论投票、对页面投票,后缀 `_up`、`_down` 依次代表赞同、反对 |
|
||||
| `name` | String | 否 | 投票用户昵称 |
|
||||
| `email` | String | 否 | 投票用户邮箱 |
|
||||
| `site_name` | String | 否 | 投票目标评论或页面所在站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 评论投票成功 |
|
||||
| `data.vote_num` | Number | - | 目标评论或页面指定类型投票数据 |
|
||||
|
||||
### Artalk 版本
|
||||
|
||||
::: warning
|
||||
|
||||
此接口较特殊,返回结果不包含 `success`、`msg`、`data` 字段,直接返回包含指定字段的 JSON。
|
||||
|
||||
:::
|
||||
|
||||
**GET/POST** `/api/version`
|
||||
|
||||
**参数** 无
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `app` | String | `artalk` | Artalk 后端程序名 |
|
||||
| `version` | String | - | Artalk 后端程序版本号 |
|
||||
| `commit_hash` | String | - | Artalk 后端程序 Git 提交哈希值 |
|
||||
| `fe_min_version` | String | - | Artalk 后端程序所需前端最低版本 |
|
||||
|
||||
## Captcha API
|
||||
|
||||
### 验证码刷新
|
||||
|
||||
**GET** `/api/captcha/refresh`
|
||||
|
||||
**参数** 无
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 验证码刷新成功 |
|
||||
| `data.img_data` | String | - | 根据 IP 获取的验证码图片 Base64 编码 |
|
||||
|
||||
### 验证码检验
|
||||
|
||||
**GET** `/api/captcha/check`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `value` | String | 是 | 验证码 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 验证码检验成功 |
|
||||
| `data.img_data` | String | - | 新的验证码图片 Base64 编码,仅验证码检验出错时存在此字段 |
|
||||
|
||||
## Admin API
|
||||
|
||||
### 评论编辑
|
||||
|
||||
**POST** `/api/admin/comment-edit`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待编辑评论 ID,**不可修改** |
|
||||
| `site_name` | String | 否 | 待编辑评论所在站点名称,**不可修改** |
|
||||
| `content` | String | 否 | 待编辑评论正文 |
|
||||
| `page_key` | String | 否 | 待编辑评论所在页面唯一标识符 |
|
||||
| `nick` | String | 否 | 待编辑评论所属用户昵称 |
|
||||
| `email` | String | 否 | 待编辑评论所属用户邮箱 |
|
||||
| `link` | String | 否 | 待编辑评论所属用户链接 |
|
||||
| `rid` | String | 否 | 待编辑评论为回复评论时传入父评论 `ID`,否则为 `0` |
|
||||
| `ua` | String | 否 | 待编辑评论所属用户 User-Agent |
|
||||
| `ip` | String | 否 | 待编辑评论所属用户 IP |
|
||||
| `is_collapsed` | Boolean | 否 | 待编辑评论是否折叠 |
|
||||
| `is_pending` | Boolean | 否 | 待编辑评论是否待审 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 评论编辑成功 |
|
||||
| `data.comment` | Object | - | 编辑评论的新数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.comment` 对象结构参考 [评论新增接口](#评论新增) `/api/add` 响应的 `data.comment`
|
||||
|
||||
:::
|
||||
|
||||
### 评论删除
|
||||
|
||||
::: danger
|
||||
|
||||
此接口不推荐外部调用。
|
||||
|
||||
调用此接口前请务必核实评论 ID,一旦调用成功将删除该评论下所有相关数据。
|
||||
|
||||
:::
|
||||
|
||||
**POST** `/api/admin/comment-del`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待删除评论 ID |
|
||||
| `site_name` | String | 是 | 待删除评论所在站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 评论删除成功 |
|
||||
|
||||
### 页面获取
|
||||
|
||||
**POST** `/api/admin/page-get`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `site_name` | String | 是 | 待获取页面目标站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 页面获取成功 |
|
||||
| `data.pages` | Array | - | 目标站点的页面数据 |
|
||||
| `data.sites` | Array | - | 所有站点数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.pages` 数组中元素结构参考 [评论获取接口](#评论获取) `/api/get` 响应的 `data.page`
|
||||
- `data.sites` 数组中元素结构参考 [站点编辑接口](#站点编辑) `/api/admin/site-edit` 响应的 `data.site`
|
||||
|
||||
:::
|
||||
|
||||
### 页面编辑
|
||||
|
||||
**POST** `/api/admin/page-edit`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待编辑页面 ID,**不可修改** |
|
||||
| `site_name` | String | 否 | 待编辑页面所在站点名称,**不可修改** |
|
||||
| `key` | String | 否 | 待编辑页面唯一标识符 |
|
||||
| `title` | String | 否 | 待编辑页面标题 |
|
||||
| `admin_only` | Boolean | 否 | 待编辑页面是否仅管理员可评论 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 页面编辑成功 |
|
||||
| `data.page` | Object | - | 编辑页面的新数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.page` 对象结构参考 [评论获取接口](#评论获取) `/api/get` 响应的 `data.page`
|
||||
|
||||
:::
|
||||
|
||||
### 页面删除
|
||||
|
||||
::: danger
|
||||
|
||||
此接口不推荐外部调用。
|
||||
|
||||
调用此接口前请务必核实页面 ID,一旦调用成功将删除该页面下所有相关数据。
|
||||
|
||||
:::
|
||||
|
||||
**POST** `/api/admin/page-del`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `key` | String | 是 | 待删除页面唯一标识符 |
|
||||
| `site_name` | String | 是 | 待删除页面所在站点名称 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 页面删除成功 |
|
||||
|
||||
### 页面更新
|
||||
|
||||
**POST** `/api/admin/page-fetch`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待更新页面 ID |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 页面更新成功 |
|
||||
| `data.page` | Object | - | 更新页面的新数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.page` 对象结构参考 [评论获取接口](#评论获取) `/api/get` 响应的 `data.page`
|
||||
|
||||
:::
|
||||
|
||||
### 站点获取
|
||||
|
||||
**POST** `/api/admin/site-get`
|
||||
|
||||
**参数** 无
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 站点获取成功 |
|
||||
| `data.sites` | Array | - | 所有站点数据 |
|
||||
|
||||
::: tip
|
||||
|
||||
- `data.sites` 数组中元素结构参考 [站点编辑接口](#站点编辑) `/api/admin/site-edit` 响应的 `data.site`
|
||||
|
||||
:::
|
||||
|
||||
### 站点新增
|
||||
|
||||
**POST** `/api/admin/site-add`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `name` | String | 是 | 待新增站点名称 |
|
||||
| `urls` | String | 是 | 待新增站点链接,如有多个需以英文半角符号 `,` 分割 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 站点新增成功 |
|
||||
| `data.site` | Object | - | 新增站点的数据 |
|
||||
| `data.site.id` | Number | - | 站点 ID |
|
||||
| `data.site.name` | String | - | 站点名称 |
|
||||
| `data.site.urls` | Array | - | 站点链接数组,元素为单个站点链接字符串 |
|
||||
| `data.site.urls_raw` | String | - | 站点链接数组合并字符串,以英文半角符号 `,` 分割 |
|
||||
| `data.site.first_url` | String | - | 站点第一链接 |
|
||||
|
||||
### 站点编辑
|
||||
|
||||
**POST** `/api/admin/site-edit`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待编辑站点 ID |
|
||||
| `name` | String | 是 | 待编辑站点新名称 |
|
||||
| `urls` | String | 是 | 待编辑站点新链接 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 站点编辑成功 |
|
||||
| `data.site` | Object | - | 编辑站点的新数据 |
|
||||
| `data.site.id` | Number | - | 站点 ID |
|
||||
| `data.site.name` | String | - | 站点名称 |
|
||||
| `data.site.urls` | Array | - | 站点链接数组,元素为单个站点链接字符串 |
|
||||
| `data.site.urls_raw` | String | - | 站点链接数组合并字符串,以英文半角符号 `,` 分割 |
|
||||
| `data.site.first_url` | String | - | 站点第一链接 |
|
||||
|
||||
### 站点删除
|
||||
|
||||
::: danger
|
||||
|
||||
此接口不推荐外部调用。
|
||||
|
||||
调用此接口前请务必核实站点 ID,一旦调用成功将删除该站点下所有相关数据。
|
||||
|
||||
:::
|
||||
|
||||
**POST** `/api/admin/site-del`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 待删除站点 ID |
|
||||
| `del_content` | Boolean | 否 | 是否删除站点所有数据,为 `false` 时仅删除站点配置而保留站点评论数据 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 站点删除成功 |
|
||||
|
||||
### 设置获取
|
||||
|
||||
**POST** `/api/admin/setting-get`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 管理员用户 ID |
|
||||
| `name` | String | 否 | 管理员用户昵称 |
|
||||
| `url` | String | 否 | 管理员用户链接 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 设置获取成功 |
|
||||
|
||||
### 设置保存
|
||||
|
||||
**POST** `/api/admin/setting-save`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `id` | Number | 是 | 管理员用户 ID |
|
||||
| `name` | String | 否 | 管理员用户昵称 |
|
||||
| `url` | String | 否 | 管理员用户链接 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 设置保存成功 |
|
||||
|
||||
### 邮件发送
|
||||
|
||||
::: warning
|
||||
|
||||
页面收到评论、评论收到回复等满足需要发送邮件提醒条件时,后端将自动向目标地址发送邮件提醒,不需手动处理邮件发送。请勿滥用此接口。
|
||||
|
||||
:::
|
||||
|
||||
**POST** `/api/admin/send-mail`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `subject` | String | 是 | 邮件主题 |
|
||||
| `body` | String | 是 | 邮件正文 |
|
||||
| `to_addr` | String | 是 | 邮件收件人 |
|
||||
|
||||
**响应**
|
||||
|
||||
| 字段名 | 数据类型 | 默认 | 说明 |
|
||||
| ----- | ------- | :-: | --- |
|
||||
| `success` | Boolean | `true` | 邮件发送成功 |
|
||||
|
||||
### Artrans 接口
|
||||
|
||||
::: warning
|
||||
|
||||
此接口暂不稳定,请避免使用!
|
||||
|
||||
:::
|
||||
|
||||
**POST** `/api/admin/artransfer`
|
||||
|
||||
**参数**
|
||||
|
||||
| 字段名 | 数据类型 | 必需 | 说明 |
|
||||
| ----- | ------- | :---: | --- |
|
||||
| `type` | String | 是 | 导入数据类型 |
|
||||
| `payload` | String | 否 | 导入数据 |
|
||||
|
||||
**响应** 无
|
||||
@@ -0,0 +1,3 @@
|
||||
# HTTP API
|
||||
|
||||
参考:<a href="/http-api.html" target="_blank">Artalk API 文档 (OpenAPI)</a>
|
||||
@@ -0,0 +1,50 @@
|
||||
# Event
|
||||
|
||||
## 基本事件
|
||||
|
||||
|事件名|描述|
|
||||
|-|-|
|
||||
| `inited` | 初始化后 |
|
||||
| `destroy` | 实例销毁时 |
|
||||
| `list-fetch` | 评论列表请求时 |
|
||||
| `list-fetched` | 评论列表请求后 |
|
||||
| `list-load` | 评论装载前 |
|
||||
| `list-loaded` | 评论装载后 |
|
||||
| `list-error` | 评论加载错误时 |
|
||||
| `list-goto-first` | 评论列表归位时 |
|
||||
| `list-reach-bottom` | 评论列表滚动到底部时 |
|
||||
| `list-reach-bottom` | 评论列表滚动到底部时 |
|
||||
| `comment-inserted` | 评论插入后 |
|
||||
| `comment-updated` | 评论更新后 |
|
||||
| `comment-deleted` | 评论删除后 |
|
||||
| `comment-rendered` | 评论节点渲染后 |
|
||||
| `unreads-updated` | 未读消息变更时 |
|
||||
| `list-goto` | 评论跳转时 |
|
||||
| `page-loaded` | 页面数据更新后 |
|
||||
| `editor-submit` | 编辑器提交时 |
|
||||
| `editor-submitted` | 编辑器提交后 |
|
||||
| `user-changed` | 本地用户数据变更时 |
|
||||
| `conf-loaded` | 配置变更时 |
|
||||
| `dark-mode-changed` | 深色模式变更时 |
|
||||
| `sidebar-show` | 侧边栏显示 |
|
||||
| `sidebar-hide` | 侧边栏隐藏 |
|
||||
|
||||
|
||||
事件声明代码:[@ArtalkJS/Artalk - types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.ts)
|
||||
|
||||
## 添加事件监听
|
||||
|
||||
```js
|
||||
artalk.on('list-loaded', () => {
|
||||
alert('评论已加载完毕')
|
||||
})
|
||||
```
|
||||
|
||||
## 解除事件监听
|
||||
|
||||
```js
|
||||
let foo = function() { /* do something */ }
|
||||
|
||||
artalk.on('list-loaded', foo)
|
||||
artalk.off('list-loaded', foo)
|
||||
```
|
||||
@@ -0,0 +1,159 @@
|
||||
# Frontend API
|
||||
|
||||
## 安装依赖
|
||||
|
||||
用你喜欢的包管理器安装 Artalk:
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
# 或者使用 npm
|
||||
npm install artalk
|
||||
```
|
||||
|
||||
## 创建实例 `init`
|
||||
|
||||
调用该函数将创建并得到一个 Artalk 的实例化对象,可用于后续操作。
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
const artalk = Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '/post/1',
|
||||
pageTitle: '关于引入 Artalk 的这档子事',
|
||||
server: 'http://your_domain:8080',
|
||||
site: 'Artalk 的博客',
|
||||
})
|
||||
```
|
||||
|
||||
调用该函数会向后端发起请求:
|
||||
|
||||
1. 获取前端配置
|
||||
2. 获取评论列表
|
||||
|
||||
配置和评论加载完毕后会分别触发 `conf-loaded` 和 `list-loaded` 事件。
|
||||
|
||||
注:前端配置可能会被覆盖,详情见:[在后端控制前端](../guide/backend/fe-control.md)。
|
||||
|
||||
## 释放资源 `destroy`
|
||||
|
||||
销毁 Artalk 实例,用于资源释放。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.destroy()
|
||||
```
|
||||
|
||||
释放资源将执行 Artalk 挂载的 DOM 节点删除,以及解除 Artalk 事件监听等操作。
|
||||
|
||||
在 Vue / React 等框架中,务必在组件销毁时调用该函数,否则会造成内存泄漏:
|
||||
|
||||
```ts
|
||||
import { onUnmounted } from 'vue'
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
```
|
||||
|
||||
## 更新配置 `update`
|
||||
|
||||
修改 Artalk 当前配置。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.update({
|
||||
// 新的配置...
|
||||
})
|
||||
```
|
||||
|
||||
调用该函数将触发 `conf-loaded` 事件。
|
||||
|
||||
更新配置不会自动刷新评论列表,可按需继续执行 `artalk.reload()` 函数。
|
||||
|
||||
请注意,该函数是一个实例化对象中的方法,并非全局函数。
|
||||
|
||||
前端配置参考:[前端配置](../guide/frontend/config.md)
|
||||
|
||||
## 重新加载 `reload`
|
||||
|
||||
重新从后端加载 Artalk 评论列表数据。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.reload()
|
||||
```
|
||||
|
||||
列表加载前将触发 `list-load` 事件,加载完毕后将触发 `list-loaded` 事件。
|
||||
|
||||
## 监听事件 `on`
|
||||
|
||||
添加 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.on('list-loaded', () => {
|
||||
alert('评论列表加载完毕')
|
||||
})
|
||||
```
|
||||
|
||||
可参考:[前端 Event](./event.md)。
|
||||
|
||||
## 解除监听 `off`
|
||||
|
||||
解除 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
const handler = () => {
|
||||
alert('评论列表加载完毕')
|
||||
}
|
||||
|
||||
artalk.on('list-loaded', handler)
|
||||
artalk.off('list-loaded', handler)
|
||||
```
|
||||
|
||||
## 触发事件 `trigger`
|
||||
|
||||
触发 Artalk 事件。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.trigger('list-loaded')
|
||||
```
|
||||
|
||||
## 加载插件 `use`
|
||||
|
||||
通过 `Artalk.use` 函数加载插件。请注意,该函数是一个全局函数,并非实例化对象中的方法。通过该函数加载的插件将对所有 Artalk 实例生效。
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
详情见:[插件开发](./plugs.md)
|
||||
|
||||
## 夜间模式 `setDarkMode`
|
||||
|
||||
修改夜间模式,可以配合博客主题调用,例如当用户点击夜间模式切换按钮。
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
## 浏览量组件 `loadCountWidget`
|
||||
|
||||
详情见:[浏览量统计](../guide/frontend/pv.md)
|
||||
@@ -0,0 +1,28 @@
|
||||
# 开发说明
|
||||
|
||||
## 参考资源
|
||||
|
||||
📖 <a href="https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md" target="_blank">开发者贡献指南 (CONTRIBUTING.md)</a>
|
||||
|
||||
🔬 <a href="/http-api.html" target="_blank">HTTP API 文档 (OpenAPI)</a>
|
||||
|
||||
🔖 <a href="https://github.com/ArtalkJS/Artalk/blob/master/CODE_OF_CONDUCT.md" target="_blank">贡献者契约行为准则 (CODE_OF_CONDUCT.md)</a>
|
||||
|
||||
|
||||
## 配置文档
|
||||
|
||||
- [后端配置](../guide/backend/config.md)
|
||||
- [前端配置](../guide/frontend/config.md)
|
||||
|
||||
## 补充说明
|
||||
|
||||
由于 Artalk 正处于开发阶段,使用此文档中 `API`、`Event` 前请务必检查时效性。
|
||||
|
||||
- `API` 部分参考源码
|
||||
* [@ArtalkJS/Artalk - src/api/](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/artalk/src/api)
|
||||
* [@ArtalkJS/Artalk - server/server.go](https://github.com/ArtalkJS/Artalk/blob/master/server/server.go)
|
||||
- `UI` 及 `Event` 部分参考源码
|
||||
* [@ArtalkJS/Artalk - src/artalk.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/artalk.ts)
|
||||
* [@ArtalkJS/Artalk - types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.ts)
|
||||
|
||||
通过 Artalk 提供的 `API` 和 `Event`,你可以实现很多高级功能,比如编写评论管理机器人、评论提醒推送插件等。Artalk 并不为此提供技术指导,但鼓励你参考此处的文档自行定制。
|
||||
@@ -0,0 +1,64 @@
|
||||
# 插件开发
|
||||
|
||||
使用 `Artalk.use` 来扩展 Artalk。
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.on('inited', () => {
|
||||
ctx.get('editor').setContent("Hello World")
|
||||
})
|
||||
|
||||
ctx.on('list-loaded', () => {
|
||||
console.log('评论列表加载完毕')
|
||||
})
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
请注意:
|
||||
|
||||
- `Artalk.use` 必须在 `Artalk.init` 之前调用。
|
||||
- 请勿依赖插件的加载顺序,请监听事件来执行插件逻辑。
|
||||
- 当所有的插件加载完毕后,会触发 `inited` 事件。
|
||||
|
||||
在 use 函数中,你可以通过 `ctx` 来访问 Artalk 的上下文。
|
||||
|
||||
## Context
|
||||
|
||||
Context 对象包含了 Artalk 的上下文信息。
|
||||
|
||||
| 成员 | 说明 |
|
||||
| --- | --- |
|
||||
| `ctx.$root` | Artalk 容器元素 |
|
||||
| `ctx.conf` | Artalk 配置 |
|
||||
| `ctx.fetch` | 加载数据数据 |
|
||||
| `ctx.reload` | 重载评论列表 |
|
||||
| `ctx.on` | 添加事件监听 |
|
||||
| `ctx.off` | 解除事件监听 |
|
||||
| `ctx.trigger` | 触发事件 |
|
||||
| `ctx.inject` | 依赖注入 |
|
||||
| `ctx.get` | 获取依赖 |
|
||||
|
||||
::: warning
|
||||
Context API 目前仍不稳定,开发可能会有变动,升级请关注 CHANGELOG。
|
||||
:::
|
||||
|
||||
参考:[@artalk/types/context.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/context.ts)
|
||||
|
||||
Artalk 拥有完整的 TypeScript 类型定义,你可以通过编辑器的自动补全来查看 API。
|
||||
|
||||
## 示例插件
|
||||
|
||||
Artalk 有很多内置的插件,你可以参考它们的源码来开发自己的插件。
|
||||
|
||||
[@ArtalkJS/Artalk - src/plugins](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/artalk/src/plugins)
|
||||
|
||||
我们还提供了一些外置的插件,同样可以参考:
|
||||
|
||||
| 插件 | 说明 |
|
||||
| --- | --- |
|
||||
| [@artalk/plugin-katex](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/plugin-katex) | LaTeX 公式插件 |
|
||||
| [@artalk/plugin-lightbox](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/plugin-lightbox) | 图片灯箱插件 |
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
title: Artalk
|
||||
titleTemplate: A Self-hosted Comment System
|
||||
|
||||
hero:
|
||||
name: Artalk
|
||||
text: A Self-hosted Comment System
|
||||
tagline: Elevate your online conversations with a comment system that embodies the trifecta of lightness, security, and refinement.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
link: /guide/intro
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/ArtalkJS/Artalk
|
||||
|
||||
features:
|
||||
- icon: 🍃
|
||||
title: Lightweight
|
||||
details: Lightweight and responsive to every interaction
|
||||
- icon: 🔒
|
||||
title: Safety
|
||||
details: Everything is under control as the data is completely self-hosted
|
||||
- icon: 🐳
|
||||
title: Turbo
|
||||
details: Easy to deploy, quick to set up commenting interface
|
||||
- icon: 🍱
|
||||
title: Flexible
|
||||
details: Use Golang to interpret the excellence of multiple platforms
|
||||
---
|
||||
@@ -0,0 +1,17 @@
|
||||
# 🥬 关于我们
|
||||
|
||||
## ~~开发团队~~有人想自我介绍一下
|
||||
|
||||
[@qwqcode](https://github.com/qwqcode):🥬 + 🦜
|
||||
|
||||
P.S. 因为侧边栏太空了,也许。所以,不知道放点什么好。被迫写了个关于页面。
|
||||
|
||||
## 团队成员
|
||||
|
||||
<sub style="text-align: center">ohhhh<span data-msg="Artalk 期待你的加入!" data-please-email="qwqcode@gmail.com" style="color:#55555;background:linear-gradient(45deg,#ff0000 0%,#ffb600 11%,#fff600 22%,#a5ff00 33%,#00a9ff 44%,#0400ff 55%,#8a00fc 66%,#ff00e9 77%,#ff0059 88%,#ff0000 100%);font-weight: bold;border-radius: 555px;">hhh.</span></sub>
|
||||
|
||||
The Artalk. Made with <span style="color: red;">♥️</span>.
|
||||
|
||||
## 关于本站
|
||||
|
||||
该站点通过 [Netlify](https://www.netlify.com/) 对 [Docs](https://github.com/ArtalkJS/Artalk/tree/master/docs) 目录进行自动部署。
|
||||
@@ -0,0 +1,397 @@
|
||||
# 多元推送
|
||||
|
||||
Artalk 支持通过多元推送功能以多种方式发送管理员通知。
|
||||
|
||||
支持 **Telegram**、**飞书**、**钉钉**、**Bark**、**Slack**、**LINE**,并且多种方式可以同时启用。
|
||||
|
||||
你可以在[控制中心](/guide/frontend/sidebar.md#控制中心)找到「设置」界面修改此配置。
|
||||
|
||||
## 配置文件
|
||||
|
||||
完整的 `admin_notify` 配置如下:
|
||||
|
||||
::: details 点击显示
|
||||
|
||||
```yaml
|
||||
# 多元推送
|
||||
admin_notify:
|
||||
# 通知模版
|
||||
notify_tpl: "default"
|
||||
noise_mode: false
|
||||
# 邮件通知管理员
|
||||
email:
|
||||
enabled: true # 当使用其他推送方式时,可以关闭管理员邮件通知
|
||||
mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复"
|
||||
mail_tpl: ""
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: false
|
||||
api_token: ""
|
||||
receivers:
|
||||
- 7777777
|
||||
# 飞书
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
# 钉钉
|
||||
ding_talk:
|
||||
enabled: false
|
||||
token: ""
|
||||
secret: ""
|
||||
# Bark
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
# Slack
|
||||
slack:
|
||||
enabled: false
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
# LINE
|
||||
line:
|
||||
enabled: false
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
# WebHook
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 邮件通知
|
||||
|
||||
通过邮件的方式向管理员发送消息通知。
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
enabled: true # 当使用其他推送方式时,可以关闭管理员邮件通知
|
||||
mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复"
|
||||
mail_tpl: ""
|
||||
```
|
||||
|
||||
当使用其他推送方式时,可以关闭管理员邮件通知。
|
||||
|
||||
在这之前,你需要配置全局邮件发送功能:[参考此处](./email.md)。
|
||||
|
||||
### 管理员邮件模板
|
||||
|
||||
- 配置项 `mail_subject` 为发送给管理员的邮件标题。
|
||||
- 配置项 `mail_tpl` 为发送给管理员的邮件选用特定的[邮件模板](./email.md#邮件模板)(填写邮件模板文件路径)。
|
||||
|
||||
(当该项留空时,将继承 `email.mail_tpl` 配置项)
|
||||
|
||||
## Telegram
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: true
|
||||
api_token: ""
|
||||
receivers:
|
||||
- 7777777
|
||||
```
|
||||
|
||||
- `api_token`:TG Bot 的 API Token。
|
||||
- `receivers`:消息接受者的数字 ID,可设置多个。
|
||||
|
||||
### 创建 TG Bot
|
||||
|
||||
搜索 `@BotFather` 回复 `/newbot` 并按提示创建新的 TG 机器人。
|
||||
|
||||

|
||||
|
||||
标红的文字就是你之后需要在 Artalk 配置中填入的 `api_token`。
|
||||
|
||||
配置中的 `receivers` 填入需要接受消息的账号数字 ID,可以搜索机器人 `@RawDataBot` 获取如图:
|
||||
|
||||

|
||||
|
||||
详情可参考:[Bots: An introduction for developers - Telegram](https://core.telegram.org/bots)
|
||||
|
||||
::: tip
|
||||
|
||||
鉴于复杂的网络环境,如需使用代理,请在 Artalk 启动之前配置环境变量,例如:
|
||||
|
||||
```sh
|
||||
export https_proxy=http://127.0.0.1:7890
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 飞书
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# 飞书
|
||||
lark:
|
||||
enabled: true
|
||||
webhook_url: ""
|
||||
```
|
||||
|
||||
- `webhook_url`:填入创建群组机器人时得到的 WebHook 地址。
|
||||
|
||||
### 创建群组机器人
|
||||
|
||||
点击顶部的加号,创建一个新的群组:
|
||||
|
||||

|
||||
|
||||
找到右侧的「群设置」-「群机器人」- 点击「添加机器人」- 选择「自定义机器人」并按照提示创建。
|
||||
|
||||
<img src="/images/notify/lark-2.png" width="700px">
|
||||
|
||||
复制如上图的 WebHook 地址,并修改 Artalk 的 `webhook_url` 配置即可。
|
||||
|
||||
<img src="/images/notify/lark-3.png" width="400px">
|
||||
|
||||
可参考:[飞书帮助中心文档](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
|
||||
|
||||
## 钉钉
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# 钉钉
|
||||
ding_talk:
|
||||
enabled: true
|
||||
token: ""
|
||||
secret: ""
|
||||
```
|
||||
|
||||
可参考:[钉钉开放文档](https://open.dingtalk.com/document/robots/custom-robot-access)
|
||||
|
||||
## Bark
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# Bark
|
||||
bark:
|
||||
enabled: true
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
```
|
||||
|
||||
[Bark](https://github.com/Finb/Bark) 是一款开源的 iOS App,并且[支持自托管](https://github.com/Finb/bark-server),你能使用 Bark 轻松地推送消息给你的 iOS 设备。
|
||||
|
||||
你可以在 App Store 搜索下载,并获得需要填入 Artalk 的 `server` 配置项:
|
||||
|
||||
|
||||
<img src="/images/notify/bark.png" width="700px">
|
||||
|
||||
|
||||
## Slack
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# Slack
|
||||
slack:
|
||||
enabled: true
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
```
|
||||
|
||||
## LINE
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# LINE
|
||||
line:
|
||||
enabled: true
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
```
|
||||
|
||||
## 通知模版
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
notify_tpl: "default"
|
||||
```
|
||||
|
||||
配置项 `admin_notify.notify_tpl` 可设置为自定义通知模版「文件路径」,默认的通知模版为:
|
||||
|
||||
```
|
||||
@{{reply_nick}}:
|
||||
|
||||
{{reply_content}}
|
||||
|
||||
{{link_to_reply}}
|
||||
```
|
||||
|
||||
可用变量和邮件模板相同,可参考:[邮件模版](./email.md#邮件模板)
|
||||
|
||||
## 嘈杂模式 `noise_mode`
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
noise_mode: false
|
||||
```
|
||||
|
||||
noise_mode 默认为关闭状态,当该项设置为 `false` 时,站内仅向管理员回复的消息会发送通知,例如「普通用户 A」回复「普通用户 B」,这两个用户之间的通讯不会通知管理员。
|
||||
|
||||
注:当 `moderator.pending_default` 为 `true` 时,noise_mode 为始终开启状态。
|
||||
|
||||
## WebHook 回调
|
||||
|
||||
开启 WebHook 后,创建新评论将以 **POST** 方式携带 `application/json` 类型的 Body 数据请求设定的 WebHook 地址。
|
||||
|
||||
你可以编写自己的 Server 端代码,处理来自 Artalk 的请求。
|
||||
|
||||
**Artalk 配置文件**
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
webhook:
|
||||
enabled: true
|
||||
url: "http://localhost:8080/"
|
||||
```
|
||||
|
||||
**Body 数据内容**
|
||||
|
||||
`application/json` 类型
|
||||
|
||||
|Key|描述|类型|备注|
|
||||
| - | - | - | - |
|
||||
|`notify_subject`|通知标题 |String| 对应 admin_notify.notify_subject 配置项 |
|
||||
|`notify_body` |通知内容 |String| 根据 admin_notify.notify_tpl 模版渲染 |
|
||||
|`comment` |评论内容 |Object| 新创建的评论数据对象 |
|
||||
|`parent_comment`|评论回复的目标|Object| 如果是根节点评论值为 null |
|
||||
|
||||
**Body 数据样本**
|
||||
|
||||
```js
|
||||
{
|
||||
"notify_subject": "",
|
||||
"notify_body": "@测试用户:\n\n测试内容\n\nhttps://127.0.0.1/index.html?atk_comment=1057",
|
||||
"comment": {
|
||||
"id": 1057,
|
||||
"content": "测试内容",
|
||||
"user_id": 226,
|
||||
"nick": "测试用户",
|
||||
"email_encrypted": "654236c1e78i4c09a17c4869c9d43910",
|
||||
"link": "https://qwqaq.com",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_4_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36",
|
||||
"date": "2022-05-23 17:00:23",
|
||||
"is_collapsed": false,
|
||||
"is_pending": false,
|
||||
"is_pinned": false,
|
||||
"is_allow_reply": false,
|
||||
"rid": 0,
|
||||
"badge_name": "",
|
||||
"badge_color": "",
|
||||
"visible": true,
|
||||
"vote_up": 0,
|
||||
"vote_down": 0,
|
||||
"page_key": "/index.html",
|
||||
"page_url": "https://127.0.0.1/index.html",
|
||||
"site_name": "ArtalkDocs"
|
||||
},
|
||||
"parent_comment": null
|
||||
}
|
||||
```
|
||||
|
||||
**Node.js express 处理示例**
|
||||
|
||||
```js
|
||||
const express = require('express');
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(express.json()); // Use JSON middleware
|
||||
|
||||
app.post('/', function(request, response){
|
||||
console.log(request.body);
|
||||
|
||||
const notifySubject = request.body.notify_subject
|
||||
const notifyBody = request.body.notify_body
|
||||
console.log(notifySubject, notifyBody);
|
||||
|
||||
response.send(request.body);
|
||||
});
|
||||
|
||||
app.listen(8080);
|
||||
```
|
||||
|
||||
**Node.js http 处理示例**
|
||||
|
||||
```js
|
||||
const http = require("http");
|
||||
|
||||
const requestListener = function (req, res) {
|
||||
// receive json request
|
||||
let body = "";
|
||||
req.on("data", function (data) {
|
||||
body += data;
|
||||
});
|
||||
req.on("end", function () {
|
||||
let json = "";
|
||||
try {
|
||||
json = JSON.parse(body);
|
||||
} catch {}
|
||||
|
||||
// do something with json
|
||||
console.log(json);
|
||||
res.end();
|
||||
});
|
||||
|
||||
res.writeHead(200);
|
||||
res.end("Hello, World!");
|
||||
};
|
||||
|
||||
const server = http.createServer(requestListener);
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
**PHP Laravel 处理示例**
|
||||
|
||||
```php
|
||||
Route::get('/', function (Request $request) {
|
||||
$data = $request->json()->all();
|
||||
$notify_subject = $data["notify_subject"];
|
||||
$notify_body = $data["notify_body"];
|
||||
});
|
||||
```
|
||||
|
||||
**Golang net/http 处理示例**
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ArtalkNotify struct {
|
||||
NotifySubject string `json:"notify_subject"`
|
||||
NotifyBody string `json:"notify_body"`
|
||||
Comment interface{} `json:"comment"`
|
||||
ParentComment interface{} `json:"parent_comment"`
|
||||
}
|
||||
|
||||
func webhookHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
var notify ArtalkNotify
|
||||
err := decoder.Decode(¬ify)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Println(notify.NotifyBody)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/webhook", webhookHandler)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# 后端构建
|
||||
|
||||
## 编译运行
|
||||
|
||||
```bash
|
||||
# 拉取代码
|
||||
git clone https://github.com/ArtalkJS/Artalk.git Artalk
|
||||
|
||||
# 编译程序
|
||||
cd Artalk && make all
|
||||
|
||||
# 配置文件
|
||||
cp conf/artalk.example.yml artalk.yml
|
||||
vim artalk.yml
|
||||
|
||||
# 运行程序
|
||||
./bin/artalk help
|
||||
./bin/artalk -c artalk.yml server
|
||||
```
|
||||
|
||||
## 构建二进制文件
|
||||
|
||||
```bash
|
||||
# 拉取代码
|
||||
git clone https://github.com/ArtalkJS/Artalk.git
|
||||
|
||||
# 执行编译
|
||||
make all
|
||||
```
|
||||
|
||||
编译二进制文件将会输出到 `bin` 目录中
|
||||
|
||||
## Docker Compose 编译运行
|
||||
|
||||
```bash
|
||||
# 拉取代码
|
||||
git clone https://github.com/ArtalkJS/Artalk
|
||||
cd Artalk
|
||||
|
||||
# 构建镜像
|
||||
docker-compose build
|
||||
|
||||
# 运行
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Docker 镜像构建
|
||||
|
||||
```bash
|
||||
# 拉取代码
|
||||
git clone https://github.com/ArtalkJS/Artalk
|
||||
cd Artalk
|
||||
|
||||
# 构建镜像
|
||||
make docker-docker
|
||||
|
||||
# 发布镜像
|
||||
make docker-push
|
||||
```
|
||||
|
||||
## DevOps
|
||||
|
||||
程序由 [GitHub Actions](https://github.com/ArtalkJS/Artalk/actions) 自动化构建
|
||||
|
||||
|版本发布|Docker 镜像构建发布|二进制构建发布|前端构建发布|
|
||||
|-|-|-|-|
|
||||
|[](https://github.com/ArtalkJS/Artalk/actions/workflows/release.yml)|[](https://github.com/ArtalkJS/Artalk/actions/workflows/build-docker.yml)|[](https://github.com/ArtalkJS/Artalk/actions/workflows/build-app.yml)|[](https://github.com/ArtalkJS/Artalk/actions/workflows/build-frontend.yml)|
|
||||
@@ -0,0 +1,160 @@
|
||||
# 验证码
|
||||
|
||||
Artalk 内置图片验证码功能,你可以配置操作频率限制,当超过限度时激活验证码。
|
||||
|
||||
此外,你也可以接入各种验证服务提供商,获得多样化的验证功能。
|
||||
|
||||
目前 Artalk 支持:Turnstile、reCAPTCHA、hCaptcha、极验。
|
||||
|
||||
你可以在[控制中心](/guide/frontend/sidebar.md#控制中心)找到「设置」界面修改此配置。
|
||||
|
||||
## 配置文件
|
||||
|
||||
完整的 `captcha` 配置如下:
|
||||
|
||||
```yaml
|
||||
# 验证码
|
||||
captcha:
|
||||
enabled: true # 总开关
|
||||
always: false # 总是需要验证码
|
||||
captcha_type: "image" # 验证码类型
|
||||
action_limit: 3 # 激活验证码所需操作次数
|
||||
action_reset: 60 # 重置操作计数器超时 (单位:s, 设为 -1 不重置)
|
||||
# Turnstile
|
||||
# (https://www.cloudflare.com/products/turnstile/)
|
||||
turnstile:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# reCaptcha
|
||||
# (https://www.google.com/recaptcha/about/)
|
||||
recaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# hCaptcha (https://www.hcaptcha.com/)
|
||||
hcaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
# Geetest 极验 (https://www.geetest.com)
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
```
|
||||
|
||||
- **always**:当该项为 `true` 时,总是需要输入验证码。
|
||||
- **captcha_type**:验证码类型,可选:`image`、`turnstile`、`recaptcha`、`hcaptcha`、`geetest`。
|
||||
- **action_limit**:激活评论所需的操作次数。
|
||||
- **action_reset**:当时间超过该值时会重置操作计数器,单位为秒,设为 `-1` 将永不重置。
|
||||
|
||||
注:当 `always` 开启时,`action_limit` 和 `action_reset` 配置将失效。
|
||||
|
||||
## 配置举例
|
||||
|
||||
#### 例 1
|
||||
|
||||
在 60s 时间范围内,当操作次数超过 3 次,将一直被要求输入验证码:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
action_limit: 3
|
||||
action_reset: 60
|
||||
```
|
||||
|
||||
在 60s 后将自动重置计数器,即重新获得 3 次不用输入验证码的机会。
|
||||
|
||||
#### 例 2
|
||||
|
||||
无论多少时间范围内,这个 IP 地址操作次数只要超过 5 次时,将一直被要求输入验证码:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
action_limit: 5
|
||||
action_reset: -1
|
||||
```
|
||||
|
||||
#### 例 3
|
||||
|
||||
总是要求输入验证码,无论这个 IP 操作多少次:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
always: true
|
||||
```
|
||||
|
||||
## 操作的定义
|
||||
|
||||
一个 IP 地址的一次「评论、投票、图片上传、密码验证」都算作一次「操作」。
|
||||
|
||||
## Turnstile
|
||||
|
||||
[Turnstile](https://www.cloudflare.com/zh-cn/products/turnstile/) 是 Cloudflare 推出的无感验证服务,可在 CF 后台申请获得 `site_key` 和 `secret_key`,之后在 Artalk 控制中心设置页填入 Key 并将 `captcha_type` 修改为 `turnstile` 即可。
|
||||
|
||||
图示:
|
||||
|
||||
<img src="../../images/captcha/cf-turnstile-1.png" width="400px">
|
||||
|
||||
<img src="../../images/captcha/cf-turnstile-2.png" width="400px">
|
||||
|
||||
对应配置文件如下:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# 省略其他配置...
|
||||
captcha_type: "turnstile"
|
||||
turnstile:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
```
|
||||
|
||||
## reCAPTCHA
|
||||
|
||||
[reCAPTCHA](https://developers.google.com/recaptcha) 是 Google 推出的验证服务,Artalk 支持接入 reCAPTCHA v3。可在谷歌开发者后台申请获得 `site_key` 和 `secret_key`,之后在 Artalk 控制中心设置页填入 Key 并将 `captcha_type` 修改为 `recaptcha` 即可。
|
||||
|
||||
对应配置文件如下:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# 省略其他配置...
|
||||
captcha_type: "recaptcha"
|
||||
recaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
```
|
||||
|
||||
注:国内访问 Google API 也许需要配置系统代理。
|
||||
|
||||
reCAPTCHA 官方提供了用于测试的 Keys:[可见此处](https://developers.google.com/recaptcha/docs/faq?hl=en#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do)。
|
||||
|
||||
## hCaptcha
|
||||
|
||||
[hCaptcha](https://www.hcaptcha.com/) 是一个验证服务,可在其官网申请获得 `site_key` 和 `secret_key`,之后在 Artalk 控制中心设置页填入 Key 并将 `captcha_type` 修改为 `hcaptcha` 即可。
|
||||
|
||||
对应配置文件如下:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# 省略其他配置...
|
||||
captcha_type: "hcaptcha"
|
||||
hcaptcha:
|
||||
site_key: ""
|
||||
secret_key: ""
|
||||
```
|
||||
|
||||
hCaptcha 官方提供了用于测试的 Keys:[可见此处](https://docs.hcaptcha.com/#integration-testing-test-keys)。
|
||||
|
||||
## Geetest 极验
|
||||
|
||||
Artalk 支持接入 [Geetest 极验](https://www.geetest.com/adaptive-captcha) 第四代行为验。
|
||||
|
||||
首先在 Geetest 官网注册账号申请获得 `captcha_id` 和 `captcha_key`,然后在 Artalk 控制中心修改配置,并将 `captcha_type` 修改为 `geetest` 即可。
|
||||
|
||||
对应配置文件如下:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# 省略其他配置...
|
||||
captcha_type: "geetest"
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
```
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
# 后端配置
|
||||
|
||||
## 轻松配置
|
||||
|
||||
推荐在侧边栏 “[控制中心](/guide/frontend/sidebar.md)” 通过图形界面修改配置,无需手动编辑配置文件。
|
||||
|
||||
## 指定配置文件路径
|
||||
|
||||
Artalk 默认以工作目录下的 `artalk.yml` 作为配置文件,可使用参数 `-c` 来指定具体文件:
|
||||
|
||||
```bash
|
||||
artalk -c ./conf.yml
|
||||
```
|
||||
|
||||
## 通过环境变量配置
|
||||
|
||||
Artalk 读取以 `ATK_` 为前缀的环境变量,并且全部大写,子节点用单个下划线表示,配置名含下划线请用双下划线表示:
|
||||
|
||||
- `_` (单下划线) 转为 `.` 表示子节点
|
||||
- `__` (双下划线) 转为 `_` 表示配置名的下划线
|
||||
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
ATK_TIMEZONE -> timezone
|
||||
ATK_LOGIN__TIMEOUT -> login_timeout
|
||||
ATK_SITE__DEFAULT -> site_default
|
||||
ATK_DB_TYPE -> db.type
|
||||
ATK_DB_TABLE__PREFIX -> db.table_prefix
|
||||
ATK_CACHE_REDIS_USERNAME -> cache.redis.username
|
||||
ATK_ADMIN_USERS_0_NAME -> admin_users[0].name
|
||||
```
|
||||
|
||||
## 获取模版配置文件
|
||||
|
||||
可参考一份「完整的配置文件」:[artalk.example.zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/conf/artalk.example.zh-CN.yml)
|
||||
|
||||
#### 使用 gen 命令生成配置文件
|
||||
|
||||
Artalk 提供 `gen` 命令,你可以快速生成一份新的配置文件:
|
||||
|
||||
```bash
|
||||
artalk gen conf -lang zh-CN ./artalk.yml
|
||||
```
|
||||
|
||||
#### 命令行下载配置文件
|
||||
|
||||
```bash
|
||||
wget -O artalk.yml https://raw.githubusercontent.com/ArtalkJS/Artalk/master/conf/artalk.example.zh-CN.yml
|
||||
```
|
||||
|
||||
## 加密密钥 `app_key`
|
||||
|
||||
在 Artalk 启动之前,你需要配置一个 `app_key` 用于对网站内容进行安全加密:
|
||||
|
||||
```yaml
|
||||
app_key: "<任意的字符>"
|
||||
```
|
||||
|
||||
## 语言 `locale`
|
||||
|
||||
设置 Artalk 的语言。遵循 Unicode BCP 47 规范,该项默认为 "zh-CN" (简体中文)。
|
||||
|
||||
```yml
|
||||
locale: "zh-CN"
|
||||
```
|
||||
|
||||
详情参考:[多语言](../frontend/i18n.md)
|
||||
|
||||
## 数据库 `db`
|
||||
|
||||
Artalk 支持连接多种数据库,支持 SQLite、MySQL、PostgreSQL、SQL Server 配置如下:
|
||||
|
||||
#### SQLite
|
||||
|
||||
SQLite 是轻型数据库,使用单个文件存储数据,无需额外运行程序,尤其适合小型站点,例如个人博客。
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: "sqlite"
|
||||
file: "./data/artalk.db"
|
||||
```
|
||||
|
||||
#### MySQL / PostgreSQL / SQL Server
|
||||
|
||||
修改 `type` 为你的数据库类型:
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: "mysql" # sqlite, mysql, pgsql, mssql
|
||||
name: "artalk" # 数据库名
|
||||
host: "localhost" # 地址
|
||||
port: "3306" # 端口
|
||||
user: "root" # 账号
|
||||
password: "" # 密码
|
||||
charset: "utf8mb4" # 编码格式
|
||||
table_prefix: "" # 表前缀 (例如:"atk_")
|
||||
ssl: false # 启用 SSL
|
||||
```
|
||||
|
||||
数据表将在 Artalk 启动时自动完成创建,无需额外操作。
|
||||
|
||||
#### 数据库连接字符串 (DSN)
|
||||
|
||||
如有需要,你还可以手动配置 `db.dsn` 来指定数据库连接字符串,例如:
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: "mysql"
|
||||
dsn: "mysql://myuser:mypassword@localhost:3306/mydatabase?tls=skip-verify"
|
||||
```
|
||||
|
||||
更多内容参考:[@go-sql-driver/mysql:README.md](https://github.com/go-sql-driver/mysql)
|
||||
|
||||
## 管理员 `admin_users`
|
||||
|
||||
你需要配置管理员账户,这样才能通过「[控制中心](../frontend/sidebar.md)」对站点内容进行管理。
|
||||
|
||||
Artalk 支持多站点,你可以创建多个管理员账户,为其分配站点,让你的朋友们共用同一个后端程序。
|
||||
|
||||
详情参考:[管理员 × 多站点](/guide/backend/multi-site.md)
|
||||
|
||||
## 可信域名 `trusted_domains`
|
||||
|
||||
```yaml
|
||||
trusted_domains:
|
||||
- "https://前端使用域名A.com"
|
||||
- "https://前端使用域名B.com"
|
||||
```
|
||||
|
||||
配置该项能限制来自列表外的 Referer 和跨域请求。
|
||||
|
||||
:::tip
|
||||
|
||||
你需要将「使用该后端的前端」URL 地址加入可信域名列表中,
|
||||
|
||||
若非默认 80/443 端口需额外附带端口号,例如:`https://example.com:8080`
|
||||
|
||||
:::
|
||||
|
||||
在侧边栏[控制中心](../frontend/sidebar.md#控制中心)「站点」选项卡 - 选择站点「修改 URL」,填入站点 URL 也具有相同的效果;添加多个 URL 可使用 `","` 英文逗号分隔,修改后请重启 Artalk。
|
||||
|
||||
可以将其关闭:
|
||||
|
||||
```yaml
|
||||
trusted_domains:
|
||||
- "*"
|
||||
```
|
||||
|
||||
::: danger
|
||||
|
||||
但并不建议这样做,关闭后将存在潜在安全风险,例如可能遭受 CSRF 跨域攻击。
|
||||
|
||||
:::
|
||||
|
||||
细节:`trusted_domains` 配置项实际上是对响应标头:
|
||||
|
||||
- `Access-Control-Allow-Origin` 的控制 (参考:[W3C Cross-Origin Resource Sharing](https://fetch.spec.whatwg.org/#http-cors-protocol))
|
||||
- `Referer` 的限制 (参考:[Referer - HTTP | MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referer))
|
||||
|
||||
CSRF 跨域攻击防范措施参考:[OWASP 安全备忘单](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
|
||||
|
||||
## 默认站点 `site_default`
|
||||
|
||||
如果你觉得大概是不会用到 Artalk 的多站点功能,可以直接将该项配置为你的站点名,例如:
|
||||
|
||||
```yaml
|
||||
site_default: "Artalk 官网"
|
||||
```
|
||||
|
||||
然后在前端直接使用这个站点名:
|
||||
|
||||
```js
|
||||
Artalk.init({ site: "Artalk 官网" })
|
||||
```
|
||||
|
||||
这样,你就无需在侧边栏的[控制中心](../frontend/sidebar.md#控制中心)手动创建站点。
|
||||
|
||||
## 前端配置 `frontend`
|
||||
|
||||
增加 `frontend` 字段内容可以在后端控制前端的配置,详情可参考:[在后端控制前端](/guide/backend/fe-control)。
|
||||
|
||||
## 邮件通知 `email`
|
||||
|
||||
配置邮件通知,让回复通过邮件的形式通知目标用户,你可以自定义邮件发送者名称、标题、模版等。
|
||||
|
||||
详情参考:[后端 · 邮件通知](/guide/backend/email.md)
|
||||
|
||||
## 多元推送 `admin_notify`
|
||||
|
||||
你可以配置多种消息发送方式,例如飞书、Telegram 等,当收到新的评论时通知管理员。
|
||||
|
||||
详情参考:[后端 · 多元推送](/guide/backend/admin_notify.md)
|
||||
|
||||
## 评论审核 `moderator`
|
||||
|
||||
配置评论审核来自动拦截垃圾评论。
|
||||
|
||||
详情参考:[后端 · 评论审核](/guide/backend/moderator.md)
|
||||
|
||||
## 验证码 `captcha`
|
||||
|
||||
支持图片、滑动验证码,通过验证码对请求频率进行限制。
|
||||
|
||||
详情参考:[后端 · 验证码](/guide/backend/captcha.md)
|
||||
|
||||
## 高速缓存 `cache`
|
||||
|
||||
为节省内存资源占用,缓存默认关闭。如果你对网站性能有较高要求,请手动开启。你还可以连接外部缓存服务器,支持 Redis 和 Memcache。
|
||||
|
||||
```yaml
|
||||
cache:
|
||||
enabled: true # 启用缓存 (默认关闭)
|
||||
type: "builtin" # 支持 redis, memcache, builtin (自带缓存)
|
||||
expires: 30 # 缓存过期时间 (单位:分钟)
|
||||
warm_up: false # 程序启动时预热缓存
|
||||
server: "" # 连接缓存服务器 (例如:"localhost:6379")
|
||||
```
|
||||
|
||||
- **warm_up**:缓存预热功能。设置为 `true`,在 Artalk 启动时会立刻对数据库内容进行全面缓存,如果你的评论数据较多,多达上万条,启动时间可能会延长。
|
||||
- **type**:缓存类型,默认为 `builtin`。可选:`redis`, `memcache`, `builtin`。
|
||||
|
||||
注:如果在 Artalk 程序外部修改数据库内容,需要刷新 Artalk 缓存才能更新。
|
||||
|
||||
---
|
||||
|
||||
Redis 身份认证、数据库配置:
|
||||
|
||||
```yaml
|
||||
# 缓存
|
||||
cache:
|
||||
# 省略其他配置项...
|
||||
redis:
|
||||
network: "tcp" # 连接方式 (tcp 或 unix)
|
||||
username: "" # 用户名
|
||||
password: "" # 密码
|
||||
db: 0 # 使用零号数据库
|
||||
```
|
||||
|
||||
<!-- 技术细节:[Artalk 缓存机制 时序图.png](/images/artalk/artalk-cache.png) -->
|
||||
<!--  -->
|
||||
|
||||
## 监听地址 `host`
|
||||
|
||||
Artalk 的默认 HTTP 端口为 23366,你可以在配置文件中指定:
|
||||
|
||||
```yaml
|
||||
host: "0.0.0.0"
|
||||
port: 23366
|
||||
```
|
||||
|
||||
配置 `host` 监听地址为 `0.0.0.0` 将 Artalk 服务暴露到全网可访问范围,
|
||||
|
||||
如果你只想让 Artalk 仅本地能够访问,可将 `host` 配置为 `127.0.0.1`。
|
||||
|
||||
命令行下启动 Artalk 时,可以携带 `--host` 和 `--port` 参数分别对地址和端口进行指定,例如:
|
||||
|
||||
```bash
|
||||
artalk server --host 127.0.0.1 --port 8080
|
||||
```
|
||||
|
||||
## 加密传输 `ssl`
|
||||
|
||||
```yaml
|
||||
ssl:
|
||||
enabled: true
|
||||
cert_path: ""
|
||||
key_path: ""
|
||||
```
|
||||
|
||||
你可以配置该项,让 HTTP 升级为 HTTPS,通过 SSL 协议加密传输数据。
|
||||
|
||||
- `cert_path`:SSL 证书公钥文件路径。
|
||||
- `key_path`:SSL 证书私钥文件路径。
|
||||
|
||||
你也可以直接反向代理 Artalk 本地服务器,然后在例如 Nginx 启用 HTTPS。
|
||||
|
||||
## 时区配置 `timezone`
|
||||
|
||||
```yaml
|
||||
timezone: "Asia/Shanghai"
|
||||
```
|
||||
|
||||
该值填写你所在地时区,对应 IANA 数据库时区名,参考:[Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) / [RFC-6557](https://www.rfc-editor.org/rfc/rfc6557.html)。
|
||||
|
||||
```
|
||||
UTC+08:00 Asia/Shanghai
|
||||
UTC+09:00 Asia/Tokyo
|
||||
UTC-07:00 America/Los_Angeles
|
||||
UTC-04:00 America/New_York
|
||||
```
|
||||
|
||||
## 登录超时 `login_timeout`
|
||||
|
||||
该值设定管理员账户登录 JWT 令牌的有效期,单位:秒。
|
||||
|
||||
例如,3 天有效:
|
||||
|
||||
```yaml
|
||||
login_timeout: 259200
|
||||
```
|
||||
|
||||
## 日志配置 `log`
|
||||
|
||||
打开日志后,系统错误等信息将被记录到设定的文件中。
|
||||
|
||||
```yaml
|
||||
log:
|
||||
enabled: true # 总开关
|
||||
filename: "./data/artalk.log" # 日志文件路径
|
||||
```
|
||||
|
||||
## 调试模式 `debug`
|
||||
|
||||
将 `debug` 配置为 `true` 启用调试模式。
|
||||
|
||||
```yaml
|
||||
debug: true
|
||||
```
|
||||
|
||||
## 工作目录 `-w` 参数
|
||||
|
||||
Artalk 在不指定工作目录的情况下,会使用「程序启动时的目录」作为工作目录。
|
||||
|
||||
```bash
|
||||
pwd # 显示当前目录路径
|
||||
```
|
||||
|
||||
使用参数 `-w` 来指定工作目录,它通常是一个「绝对路径」,例如:
|
||||
|
||||
```bash
|
||||
artalk -w /root/artalk -c ./conf.yml
|
||||
```
|
||||
|
||||
注:`-c` 的相对路径会基于 `-w` 的路径,Artalk 此时会读取 `/root/artalk/conf.yml` 作为配置文件。
|
||||
|
||||
其次,在「配置文件中」使用的「相对路径」,也会基于「工作目录」。
|
||||
|
||||
例如 `conf.yml` 中有这样的配置:
|
||||
|
||||
```yaml
|
||||
test_file: "./data/artalk.log"
|
||||
```
|
||||
|
||||
将读取 `/root/artalk/data/artalk.log`。
|
||||
|
||||
::: tip
|
||||
|
||||
配置文件相关代码:[/internal/config/config.go](https://github.com/ArtalkJS/Artalk/blob/master/internal/config/config.go)
|
||||
|
||||
前往:[前端配置](/guide/frontend/config.md)
|
||||
:::
|
||||
@@ -0,0 +1,144 @@
|
||||
# 守护进程
|
||||
|
||||
## Systemd
|
||||
|
||||
前提条件:
|
||||
|
||||
- Artalk 二进制文件,可在 [GitHub Release](https://github.com/ArtalkJS/Artalk/releases) 下载
|
||||
- `systemctl --version` 232 或更新的版本
|
||||
- `sudo` 管理员权限
|
||||
|
||||
移动 Artalk 到 `$PATH`,例如:
|
||||
|
||||
```bash
|
||||
sudo mv artalk /usr/bin/
|
||||
```
|
||||
|
||||
测试是否有效:
|
||||
|
||||
```bash
|
||||
artalk version
|
||||
```
|
||||
|
||||
创建名为 artalk 的用户组:
|
||||
|
||||
```bash
|
||||
sudo groupadd --system artalk
|
||||
```
|
||||
|
||||
创建一个名为 artalk 的用户,并且拥有一个可写的 home 目录:
|
||||
|
||||
```bash
|
||||
sudo useradd --system \
|
||||
--gid artalk \
|
||||
--create-home \
|
||||
--home-dir /var/lib/artalk \
|
||||
--shell /usr/sbin/nologin \
|
||||
--comment "Artalk server" \
|
||||
artalk
|
||||
```
|
||||
|
||||
如果你有 Artalk 的配置文件,请确保刚刚创建的 artalk 用户有权限可以读取。
|
||||
|
||||
创建服务文件:
|
||||
|
||||
```bash
|
||||
sudo vim /etc/systemd/system/artalk.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Artalk
|
||||
Documentation=https://artalk.js.org
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=artalk
|
||||
Group=artalk
|
||||
ExecStart=/usr/bin/artalk server -w /var/lib/artalk -c /etc/artalk/artalk.yml
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
ExecStop=/bin/kill -s QUIT $MAINPID
|
||||
TimeoutStopSec=5s
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=512
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
请仔细检查 `ExecStart` 和 `ExecReload`。 确保二进制文件的位置和程序启动参数是正确的
|
||||
|
||||
例如:更改 `-c` 参数的路径来指定配置文件,`-w` 参数可以更改工作目录。
|
||||
|
||||
请注意配置文件中的所有相对路径都是基于工作目录,例如配置文件中的 `./data/` 文件夹,如果启动参数 `-w /var/lib/artalk`,将读取 `/var/lib/artalk/data/` 目录。请确保该目录中的文件对于创建的 `artalk` 账户有权限可读写。
|
||||
|
||||
保存服务文件后,你可以设置自动启动服务:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now artalk
|
||||
```
|
||||
|
||||
验证服务是否正常运行:
|
||||
|
||||
```bash
|
||||
systemctl status artalk
|
||||
```
|
||||
|
||||
一些常用的命令:
|
||||
|
||||
- 启动服务:`systemctl start artalk`
|
||||
- 停止服务:`systemctl stop artalk`
|
||||
- 查看状态:`systemctl status artalk`
|
||||
- 查看日志:`journalctl -u artalk --no-pager | less +G`
|
||||
|
||||
## Tmux
|
||||
|
||||
tmux 将创建一个持续的命令行会话,在 SSH 或 tty 断开后保持在后台。
|
||||
|
||||
Note: 服务器关闭或重启后,tmux 会话将被清除,需要手动重新运行程序。
|
||||
|
||||
1. 创建会话 `tmux new -s artalk`
|
||||
2. 运行程序 `./artalk server`
|
||||
|
||||
恢复接入会话:`tmux attach -t artalk`
|
||||
|
||||
查看所有会话:`tmux ls`
|
||||
|
||||
## Supervisor
|
||||
|
||||
以宝塔面板举例:打开「软件商店」,搜索并安装「Supervisor管理器」:
|
||||
|
||||

|
||||
|
||||
安装后,打开插件,点击「添加守护程序」:
|
||||
|
||||

|
||||
|
||||
> - 启动用户:`root` 或其他
|
||||
> - 运行目录:点击右侧图标,选择 Artalk 所在目录
|
||||
> - 启动命令:`./artalk server`
|
||||
|
||||
## Docker
|
||||
|
||||
更新 Docker 容器的 [Restart 策略](https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) 以达到进程守护效果。
|
||||
|
||||
```bash
|
||||
docker update --restart=unless-stopped artalk
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
在 `docker-compose.yml` 文件给服务添加 `restart: unless-stopped` 策略:
|
||||
|
||||
```diff
|
||||
version: '3'
|
||||
services:
|
||||
artalk:
|
||||
+ restart: unless-stopped
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
# 数据
|
||||
|
||||
## 备份
|
||||
|
||||
前往:[数据迁移 · 备份](/guide/transfer.md)
|
||||
@@ -0,0 +1,115 @@
|
||||
# Docker
|
||||
|
||||
Artalk 提供后端程序的 Docker 镜像,以便加速部署流程,提供一个良好的部署体验。
|
||||
|
||||
[Docker Hub](https://hub.docker.com/r/artalk/artalk-go) 镜像版本随代码仓库的 [Releases](https://github.com/ArtalkJS/Artalk/releases) 保持同步。
|
||||
|
||||
## 镜像拉取
|
||||
|
||||
`docker pull artalk/artalk-go`
|
||||
|
||||
## 容器创建
|
||||
|
||||
:::tip
|
||||
|
||||
推荐使用 Docker Compose,可参考 [程序部署](/guide/deploy) 页面的步骤。
|
||||
|
||||
:::
|
||||
|
||||
Docker 容器创建:
|
||||
|
||||
```bash
|
||||
# 为 Artalk 创建一个目录
|
||||
mkdir Artalk
|
||||
cd Artalk
|
||||
|
||||
# 拉取 docker 镜像
|
||||
docker pull artalk/artalk-go
|
||||
|
||||
# 生成配置文件
|
||||
docker run -it -v $(pwd)/data:/data --rm artalk/artalk-go gen config data/artalk.yml
|
||||
|
||||
# 编辑配置文件
|
||||
vim data/artalk.yml
|
||||
|
||||
# 运行 docker 容器
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 0.0.0.0:8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
然后,在前端配置填入后端地址:
|
||||
|
||||
```js
|
||||
Artalk.init({ server: "http://your_domain:8080" })
|
||||
```
|
||||
|
||||
## 重启
|
||||
|
||||
修改配置文件后,需要重启才能生效。
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker-compose restart
|
||||
|
||||
# Docker
|
||||
docker restart artalk
|
||||
```
|
||||
|
||||
## 停止
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker-compose stop
|
||||
|
||||
# Docker
|
||||
docker stop artalk
|
||||
```
|
||||
|
||||
## 升级
|
||||
|
||||
删除现有容器,拉取最新镜像,然后重新创建容器即可。
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
docker-compose down
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker stop artalk
|
||||
docker rm artalk
|
||||
docker pull artalk/artalk-go
|
||||
```
|
||||
|
||||
::: tip
|
||||
突破性变动请注意查看版本更新日志中的提示:[CHANGELOG.md](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md)
|
||||
:::
|
||||
|
||||
## 拉取历史镜像
|
||||
|
||||
镜像会随代码仓库 tags 自动构建发布,您可拉取不同版本号的镜像。
|
||||
|
||||
```bash
|
||||
docker pull artalk/artalk-go@版本号
|
||||
```
|
||||
|
||||
## 进入容器
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker-compose exec artalk bash
|
||||
|
||||
# Docker
|
||||
docker exec -it artalk bash
|
||||
```
|
||||
|
||||
## 多平台兼容性
|
||||
|
||||
Docker 镜像暂仅提供 x86、arm64 的镜像构建,如需更多平台架构版本,请下载 [二进制构建部署](/guide/deploy.md#普通方式部署)。
|
||||
@@ -0,0 +1,327 @@
|
||||
# 邮件通知
|
||||
|
||||
Artalk 支持 SMTP 协议、阿里云邮件推送、调用系统 sendmail 命令等方式向用户发送邮件通知。
|
||||
|
||||
你可以在[控制中心](/guide/frontend/sidebar.md#控制中心)找到「设置」界面修改此配置。
|
||||
|
||||
## 配置文件
|
||||
|
||||
完整的 `email` 配置如下:
|
||||
|
||||
```yaml
|
||||
# 邮件通知
|
||||
email:
|
||||
enabled: false # 总开关
|
||||
send_type: "smtp" # 发送方式 [smtp, ali_dm, sendmail]
|
||||
send_name: '{{reply_nick}}' # 发信人昵称
|
||||
send_addr: "example@qq.com" # 发信人地址
|
||||
mail_subject: "[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复"
|
||||
mail_tpl: "default" # 邮件模板文件
|
||||
smtp:
|
||||
host: "smtp.qq.com"
|
||||
port: 587
|
||||
username: "example@qq.com"
|
||||
password: ""
|
||||
ali_dm: # https://help.aliyun.com/document_detail/29444.html
|
||||
access_key_id: "" # 阿里云颁发给用户的访问服务所用的密钥 ID
|
||||
access_key_secret: "" # 用于加密的密钥
|
||||
account_name: "example@example.com" # 管理控制台中配置的发信地址
|
||||
```
|
||||
|
||||
### 选择发件方式
|
||||
|
||||
配置项 `enabled` 启用邮件,`send_type` 用于选择发送方式,可选:`smtp`, `ali_dm`, `sendmail`。
|
||||
|
||||
```yaml
|
||||
email:
|
||||
enabled: true
|
||||
send_type: "smtp" # 发送方式
|
||||
# 省略其他配置...
|
||||
smtp:
|
||||
# SMTP 配置...
|
||||
ali_dm:
|
||||
# 阿里云推送配置...
|
||||
```
|
||||
|
||||
### SMTP 配置
|
||||
|
||||
```yaml
|
||||
# 邮件通知
|
||||
email:
|
||||
enabled: true
|
||||
send_type: "smtp" # 选择 smtp
|
||||
smtp:
|
||||
host: "smtp.qq.com"
|
||||
port: 587
|
||||
username: "example@qq.com"
|
||||
password: ""
|
||||
```
|
||||
|
||||
### 阿里云推送配置
|
||||
|
||||
```yaml
|
||||
email:
|
||||
enabled: true
|
||||
send_type: "ali_dm" # 选择 ali_dm
|
||||
ali_dm:
|
||||
access_key_id: "" # 阿里云颁发给用户的访问服务所用的密钥 ID
|
||||
access_key_secret: "" # 用于加密的密钥
|
||||
account_name: "example@example.com" # 管理控制台中配置的发信地址
|
||||
```
|
||||
|
||||
可参考:[阿里云官方文档](https://help.aliyun.com/document_detail/29444.html)
|
||||
|
||||
## 评论回复
|
||||
|
||||
邮件中会有一个评论回复按钮,该链接指向前端给定的页面 PageKey,若你提供的 `pageKey` 配置项为页面的「相对路径」,你需要在「[控制中心](../frontend/sidebar.md#控制中心)」-「站点」为你的站点设置一个 URL:
|
||||
|
||||
<img src="/images/sidebar/site_url.png" width="400px">
|
||||
|
||||
## 邮件模板
|
||||
|
||||
### 模板变量
|
||||
|
||||
你可以在 `mail_subject` 和 `mail_subject_to_admin` 以及邮件模板文件中使用模板变量:
|
||||
|
||||
```yaml
|
||||
email:
|
||||
# 省略其他配置...
|
||||
mail_subject: "[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复"
|
||||
mail_subject_to_admin: '[{{site_name}}] 您的文章 "{{page_title}}" 有新回复'
|
||||
mail_tpl: "default" # 邮件模板文件
|
||||
```
|
||||
|
||||
变量是 “Mustache” 的语法,`双大括号` + `变量名` 的形式即可输出一个变量:
|
||||
|
||||
**基本内容变量**
|
||||
|
||||
```
|
||||
{{content}} # 评论内容
|
||||
{{link_to_reply}} # 回复链接
|
||||
{{nick}} # 评论者昵称
|
||||
{{page_title}} # 页面标题
|
||||
{{page_url}} # 页面 PageKey (URL)
|
||||
{{reply_content}} # 回复对象的内容
|
||||
{{reply_nick}} # 回复对象的昵称f
|
||||
{{site_name}} # 站点名
|
||||
{{site_url}} # 站点 URL
|
||||
```
|
||||
|
||||
::: details 查看其他变量
|
||||
|
||||
```
|
||||
# 评论创建者
|
||||
{{comment.badge_color}}
|
||||
{{comment.badge_name}}
|
||||
{{comment.content}}
|
||||
{{comment.content_raw}}
|
||||
{{comment.date}}
|
||||
{{comment.datetime}}
|
||||
{{comment.email}}
|
||||
{{comment.email_encrypted}}
|
||||
{{comment.id}}
|
||||
{{comment.is_allow_reply}}
|
||||
{{comment.is_collapsed}}
|
||||
{{comment.is_pending}}
|
||||
{{comment.link}}
|
||||
{{comment.nick}}
|
||||
{{comment.page.admin_only}}
|
||||
{{comment.page.id}}
|
||||
{{comment.page.key}}
|
||||
{{comment.page.site_name}}
|
||||
{{comment.page.title}}
|
||||
{{comment.page.url}}
|
||||
{{comment.page.vote_down}}
|
||||
{{comment.page.vote_up}}
|
||||
{{comment.page_key}}
|
||||
{{comment.page_title}}
|
||||
{{comment.rid}}
|
||||
{{comment.site.first_url}}
|
||||
{{comment.site.id}}
|
||||
{{comment.site.name}}
|
||||
{{comment.site.urls.0}}
|
||||
{{comment.site.urls_raw}}
|
||||
{{comment.site_name}}
|
||||
{{comment.time}}
|
||||
{{comment.ua}}
|
||||
{{comment.visible}}
|
||||
{{comment.vote_down}}
|
||||
{{comment.vote_up}}
|
||||
|
||||
# 父评论(评论创建者回复的评论)
|
||||
{{parent_comment.badge_color}}
|
||||
{{parent_comment.badge_name}}
|
||||
{{parent_comment.content}}
|
||||
{{parent_comment.content_raw}}
|
||||
{{parent_comment.date}}
|
||||
{{parent_comment.datetime}}
|
||||
{{parent_comment.email}}
|
||||
{{parent_comment.email_encrypted}}
|
||||
{{parent_comment.id}}
|
||||
{{parent_comment.is_allow_reply}}
|
||||
{{parent_comment.is_collapsed}}
|
||||
{{parent_comment.is_pending}}
|
||||
{{parent_comment.link}}
|
||||
{{parent_comment.nick}}
|
||||
{{parent_comment.page.admin_only}}
|
||||
{{parent_comment.page.id}}
|
||||
{{parent_comment.page.key}}
|
||||
{{parent_comment.page.site_name}}
|
||||
{{parent_comment.page.title}}
|
||||
{{parent_comment.page.url}}
|
||||
{{parent_comment.page.vote_down}}
|
||||
{{parent_comment.page.vote_up}}
|
||||
{{parent_comment.page_key}}
|
||||
{{parent_comment.page_title}}
|
||||
{{parent_comment.rid}}
|
||||
{{parent_comment.site.first_url}}
|
||||
{{parent_comment.site.id}}
|
||||
{{parent_comment.site.name}}
|
||||
{{parent_comment.site.urls}}
|
||||
{{parent_comment.site.urls_raw}}
|
||||
{{parent_comment.site_name}}
|
||||
{{parent_comment.time}}
|
||||
{{parent_comment.ua}}
|
||||
{{parent_comment.visible}}
|
||||
{{parent_comment.vote_down}}
|
||||
{{parent_comment.vote_up}}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: details 查看数据样例
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "<p>测试内容</p>\n",
|
||||
"link_to_reply": "https://artalk.js.org/?atk_comment=8100&atk_notify_key=44a9b2f08312565fba47c716df9d177f",
|
||||
"nick": "用户名",
|
||||
"page_title": "Artalk",
|
||||
"page_url": "https://artalk.js.org/",
|
||||
|
||||
"reply_content": "<p>回复者内容</p>\n",
|
||||
"reply_nick": "回复者",
|
||||
"site_name": "ArtalkDemo",
|
||||
"site_url": "http://localhost:3000/",
|
||||
|
||||
"comment.badge_color": "",
|
||||
"comment.badge_name": "",
|
||||
"comment.content": "<p>回复者内容</p>\n",
|
||||
"comment.content_raw": "回复者内容",
|
||||
"comment.date": "2021-11-22",
|
||||
"comment.datetime": "2021-11-22 22:22:42",
|
||||
"comment.email": "replyer@example.com",
|
||||
"comment.email_encrypted": "249898bd50e0febc5799485cf10b345a",
|
||||
"comment.id": 8100,
|
||||
"comment.is_allow_reply": true,
|
||||
"comment.is_collapsed": false,
|
||||
"comment.is_pending": false,
|
||||
"comment.link": "",
|
||||
"comment.nick": "回复者",
|
||||
"comment.page.admin_only": false,
|
||||
"comment.page.id": 75,
|
||||
"comment.page.key": "https://artalk.js.org/",
|
||||
"comment.page.site_name": "ArtalkDemo",
|
||||
"comment.page.title": "Artalk",
|
||||
"comment.page.url": "https://artalk.js.org/",
|
||||
"comment.page.vote_down": 0,
|
||||
"comment.page.vote_up": 0,
|
||||
"comment.page_key": "https://artalk.js.org/",
|
||||
"comment.page_title": "Artalk",
|
||||
"comment.rid": 8099,
|
||||
"comment.site.first_url": "http://localhost:3000/",
|
||||
"comment.site.id": 2,
|
||||
"comment.site.name": "ArtalkDemo",
|
||||
"comment.site.urls.0": "http://localhost:3000/",
|
||||
"comment.site.urls_raw": "http://localhost:3000/",
|
||||
"comment.site_name": "ArtalkDemo",
|
||||
"comment.time": "22:22:42",
|
||||
"comment.ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1",
|
||||
"comment.visible": false,
|
||||
"comment.vote_down": 0,
|
||||
"comment.vote_up": 0,
|
||||
|
||||
"parent_comment.badge_color": "",
|
||||
"parent_comment.badge_name": "",
|
||||
"parent_comment.content": "<p>测试内容</p>\n",
|
||||
"parent_comment.content_raw": "测试内容",
|
||||
"parent_comment.date": "2021-11-22",
|
||||
"parent_comment.datetime": "2021-11-22 22:21:17",
|
||||
"parent_comment.email": "test@example.com",
|
||||
"parent_comment.email_encrypted": "55502f40dc8b7c769880b10874abc9d0",
|
||||
"parent_comment.id": 8099,
|
||||
"parent_comment.is_allow_reply": true,
|
||||
"parent_comment.is_collapsed": false,
|
||||
"parent_comment.is_pending": false,
|
||||
"parent_comment.link": "https://qwqaq.com",
|
||||
"parent_comment.nick": "用户名",
|
||||
"parent_comment.page.admin_only": false,
|
||||
"parent_comment.page.id": 75,
|
||||
"parent_comment.page.key": "https://artalk.js.org/",
|
||||
"parent_comment.page.site_name": "ArtalkDemo",
|
||||
"parent_comment.page.title": "Artalk",
|
||||
"parent_comment.page.url": "https://artalk.js.org/",
|
||||
"parent_comment.page.vote_down": 0,
|
||||
"parent_comment.page.vote_up": 0,
|
||||
"parent_comment.page_key": "https://artalk.js.org/",
|
||||
"parent_comment.page_title": "Artalk",
|
||||
"parent_comment.rid": 0,
|
||||
"parent_comment.site.first_url": "http://localhost:3000/",
|
||||
"parent_comment.site.id": 2,
|
||||
"parent_comment.site.name": "ArtalkDemo",
|
||||
"parent_comment.site.urls.0": "http://localhost:3000/",
|
||||
"parent_comment.site.urls_raw": "http://localhost:3000/",
|
||||
"parent_comment.site_name": "ArtalkDemo",
|
||||
"parent_comment.time": "22:21:17",
|
||||
"parent_comment.ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36",
|
||||
"parent_comment.visible": false,
|
||||
"parent_comment.vote_down": 0,
|
||||
"parent_comment.vote_up": 0,
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 自定义模板
|
||||
|
||||
你可以将 `mail_tpl` 设置为一个「具体的文件路径」,使用外部的自定义邮件模板。
|
||||
|
||||
例如,将 `mail_tpl` 配置为 `"/root/Artalk/data/mail_tpl/your_email_template.html"`
|
||||
|
||||
```yaml
|
||||
email:
|
||||
mail_tpl: "/root/Artalk/data/mail_tpl/your_email_template.html"
|
||||
# 其他配置省略...
|
||||
```
|
||||
|
||||
那么,在这个路径应该有一个文件:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<p>Hi, {{nick}}:</p>
|
||||
<p>
|
||||
您在 “{{page_title}}” 收到了回复:
|
||||
</p>
|
||||
<div>
|
||||
<div>@{{reply_nick}}:</div>
|
||||
<div>{{reply_content}}</div>
|
||||
</div>
|
||||
<p><a href="{{link_to_reply}}" target="_blank">回复消息 »</a></p>
|
||||
</div>
|
||||
```
|
||||
|
||||
Artalk 内置许多预设的邮件模板,例如 `mail_tpl: "default"` 使用的就是:[@ArtalkJS/Artalk:/internal/template/email_tpl/default.html](https://github.com/ArtalkJS/Artalk/blob/master/internal/template/email_tpl/default.html)
|
||||
|
||||
## 发向管理员的邮件
|
||||
|
||||
邮件通知目标为管理员和普通用户,你可通过如下配置,为发向管理员的邮件设定不同的标题:
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
enabled: true
|
||||
mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复"
|
||||
```
|
||||
|
||||
注:旧版 `email.mail_subject_to_admin` 配置项已弃用,请使用以上替代。
|
||||
|
||||
不局限于邮件,Artalk 支持多种方式向管理员发送通知,参考:[多元推送](./admin_notify.md#邮件通知)。
|
||||
@@ -0,0 +1,70 @@
|
||||
# 在后端控制前端
|
||||
|
||||
你可以在后端完全控制前端界面的行为(包括设定[前端的配置](/guide/frontend/config)),我们推荐使用这种方式部署 Artalk,并且这个特性「默认开启」。
|
||||
|
||||
## 如何修改前端配置
|
||||
|
||||
在侧边栏的[控制中心](/guide/frontend/sidebar.md#控制中心)提供了图形设置界面,你能快速轻松地对前端配置进行修改。
|
||||
|
||||
<img src="../../images/sidebar/fe-setting.png" width="400px">
|
||||
|
||||
## 在前端引入内置资源
|
||||
|
||||
后端 Artalk 服务器中内置了可供前端引入的 JS 和 CSS 资源文件:
|
||||
|
||||
```html
|
||||
<!-- CSS -->
|
||||
<link href="https://<artalk_go_server>/dist/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="https://<artalk_go_server>/dist/Artalk.js"></script>
|
||||
```
|
||||
|
||||
> 提示:将 `<artalk_go_server>` 替换为你的 Artalk 服务器地址。
|
||||
|
||||
这样能让前后端始终保持兼容性,而无需在程序升级后手动更换 Artalk 前端资源的引入地址。
|
||||
|
||||
## 进阶内容
|
||||
|
||||
### 手动编辑配置文件
|
||||
|
||||
Artalk 提供图形界面简化配置,一般情况无需手动修改。
|
||||
|
||||
在 Artalk 的配置文件 `artalk.yml` 中配置 `frontend` 字段来控制前端界面,例如:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
placeholder: "键入内容..."
|
||||
noComment: "「此时无声胜有声」"
|
||||
sendBtn: "发送评论"
|
||||
emoticons: "https://raw.githubusercontent.com/ArtalkJS/Emoticons/master/grps/default.json"
|
||||
# ----- 此处省略 -------
|
||||
# 与前端配置项名称保持一致
|
||||
```
|
||||
|
||||
一份完整的后端 `frontend` 字段配置文件可供参考:[artalk.example.zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/conf/artalk.example.zh-CN.yml)
|
||||
|
||||
### 关闭 “后端控制前端” 功能
|
||||
|
||||
后端控制前端功能默认开启,我们不建议关闭该功能。
|
||||
|
||||
在前端 `Artalk.init({ ... })` 时定义的配置会被配置文件中的 `frontend` 字段配置所覆盖,但如果有需要也可以在前端禁用这个特性:
|
||||
|
||||
```diff
|
||||
Artalk.init({
|
||||
+ useBackendConf: false,
|
||||
})
|
||||
```
|
||||
|
||||
### 表情包配置
|
||||
|
||||
建议 `frontend.emoticons` 填入表情包的 URL,而非 Object。
|
||||
|
||||
参考文档:[表情包](/guide/frontend/emoticons)
|
||||
|
||||
如需传递 Object,可以将其转为 JSON 字符串格式,例如:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
emoticons: '{"表情": { "test": "tttt..." }}'
|
||||
```
|
||||
@@ -0,0 +1,94 @@
|
||||
# 图片上传
|
||||
|
||||
Artalk 提供图片上传功能,支持限制图片大小、上传频率等,你还能结合 upgit 将图片上传到图床。
|
||||
|
||||
你可以在[控制中心](/guide/frontend/sidebar.md#控制中心)找到「设置」界面修改此配置。
|
||||
|
||||
## 配置文件
|
||||
|
||||
完整的 `img-upload` 配置如下:
|
||||
|
||||
```yaml
|
||||
# 图片上传
|
||||
img_upload:
|
||||
enabled: true # 总开关
|
||||
path: "./data/artalk-img/" # 图片存放路径
|
||||
max_size: 5 # 图片大小限制 (单位:MB)
|
||||
public_path: null # 指定图片链接基础路径 (默认为 "/static/images/")
|
||||
# 使用 upgit 将图片上传到 GitHub 或图床
|
||||
upgit:
|
||||
enabled: false # 启用 upgit
|
||||
exec: "./upgit -c <upgit配置文件路径> -t /artalk-img"
|
||||
del_local: true # 上传后删除本地的图片
|
||||
```
|
||||
|
||||
## 使用 Upgit 上传到图床
|
||||
|
||||
[Upgit](https://github.com/pluveto/upgit) 支持将图片上传到 Github、Gitee、腾讯云 COS、七牛云、又拍云、SM.MS 等图床或代码仓库。
|
||||
|
||||
首先,根据 [README.md](https://github.com/pluveto/upgit) 的说明,下载 Upgit 并完成你需要上传的目标图床的配置。
|
||||
|
||||
然后在 Artalk 的 `img_upload.upgit` 字段填入 Upgit 启动参数 (建议使用程序绝对路径),例如:
|
||||
|
||||
```yaml
|
||||
upgit:
|
||||
enabled: true # 启用 upgit
|
||||
exec: "/root/upgit -c <upgit配置文件路径> -t /artalk-img"
|
||||
del_local: true # 上传后删除本地的图片
|
||||
```
|
||||
|
||||
## 上传频率限制
|
||||
|
||||
频率限制跟随 `captcha` 验证码配置,当超出限制将弹出验证码。
|
||||
|
||||
可参考:[后端 · 验证码](/guide/backend/captcha.md)
|
||||
|
||||
## path
|
||||
|
||||
`img_upload.path` 为上传的图片文件「本地存放目录」路径,该目录会被 Artalk 映射到可访问的:
|
||||
|
||||
```
|
||||
http://<后端地址>/static/images/
|
||||
```
|
||||
|
||||
## public_path
|
||||
|
||||
`img.public_path` 为空的默认值为:`/static/images/`
|
||||
|
||||
当该项为「相对路径」时,例如:`/static/images/` 前端上传图片得到的 HTML 标签将为:
|
||||
|
||||
```html
|
||||
<img src="http://<后端地址>/static/images/1.png">
|
||||
```
|
||||
|
||||
注:这里的 `<后端地址>` 是前端 `conf.server` 配置。
|
||||
|
||||
当该项为「完整 URL 路径」时,例如:`https://cdn.github.com/img/` 时,图片标签将为:
|
||||
|
||||
```html
|
||||
<img src="https://cdn.github.com/img/1.png">
|
||||
```
|
||||
|
||||
提示:这个配置可以结合负载均衡等场景使用。
|
||||
|
||||
## 在前端自定义上传 API
|
||||
|
||||
前端提供了配置项 `imgUploader`,你可以自定义前端图片上传时请求的 API,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
imgUploader: async (file) => {
|
||||
const form = new FormData()
|
||||
form.set('file', file)
|
||||
|
||||
const imgUrl = await fetch("https://api.example.org/upload", {
|
||||
method: 'POST',
|
||||
body: form
|
||||
})
|
||||
|
||||
return imgUrl
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
参考:[前端配置文档](../frontend/config.md#imguploader)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Artalk
|
||||
|
||||
Artalk 拥有一个 Golang 语言编写的后端程序。
|
||||
|
||||
[GitHub 代码仓库](https://github.com/ArtalkJS/Artalk)
|
||||
|
||||
- 高效快速
|
||||
- 异步执行
|
||||
- 跨平台兼容
|
||||
- 轻量级部署
|
||||
|
||||
## Supports
|
||||
|
||||
- 运行环境:支持 Linux, Windows, Darwin (x64 + ARM)
|
||||
- 数据存储:支持 SQLite, MySQL, PostgreSQL, SQL Server
|
||||
- 邮件发送:支持 SMTP, 阿里云邮件, 调用 sendmail 发送邮件
|
||||
- 高效缓存:支持 Redis, Memcache, In-Memory (BigCache)
|
||||
@@ -0,0 +1,3 @@
|
||||
# 后端部署
|
||||
|
||||
参考:[程序部署](../deploy.md)
|
||||
@@ -0,0 +1,118 @@
|
||||
# 评论审核
|
||||
|
||||
Artalk 支持多种评论过滤方式以拦截垃圾评论,支持通过 Akismet、腾讯云内容安全、阿里云内容安全 等在线 API 实现垃圾评论识别和拦截,也可通过本地离线关键词词库过滤评论,结合[验证码](./captcha.md)功能进一步提高评论审核强度,让垃圾评论没有容身之地。
|
||||
|
||||
你可以在[控制中心](/guide/frontend/sidebar.md#控制中心)找到「设置」界面修改此配置。
|
||||
|
||||
## 配置文件
|
||||
|
||||
完整的 `moderator` 配置如下:
|
||||
|
||||
```yaml
|
||||
# 评论审核
|
||||
moderator:
|
||||
pending_default: false # 发表新评论默认为 “待审状态”
|
||||
api_fail_block: false # 垃圾检测 API 请求错误仍然拦截
|
||||
# akismet.com 反垃圾
|
||||
akismet_key: ""
|
||||
# 腾讯云文本内容安全 (tms)
|
||||
tencent: # https://cloud.tencent.com/document/product/1124/64508
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
# 阿里云内容安全
|
||||
aliyun: # https://help.aliyun.com/document_detail/28417.html
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
# 关键词词库过滤
|
||||
keywords:
|
||||
enabled: false
|
||||
pending: false # 匹配成功设为待审状态
|
||||
files: # 支持多个词库文件
|
||||
- "./data/词库_1.txt"
|
||||
file_sep: "\n" # 词库文件内容分割符
|
||||
replac_to: "x" # 替换字符
|
||||
```
|
||||
|
||||
## 默认待审模式
|
||||
|
||||
开启发表新评论默认为 “待审状态”:
|
||||
|
||||
```yaml
|
||||
moderator:
|
||||
pending_default: true
|
||||
```
|
||||
|
||||
## Akismet
|
||||
|
||||
[Akismet](https://akismet.com/) 是 WordPress 提供的面向全球范围的老牌垃圾拦截 API,通常对一些英文的垃圾评论十分凑效。Akismet 提供了 Personal 免费版本,适用于个人博客站点。
|
||||
|
||||

|
||||
|
||||
你能在 [Akismet 官网](https://akismet.com/) 轻松地申请 `akismet_key`,并填入配置文件中,即可启用 Akismet 垃圾拦截。
|
||||
|
||||

|
||||
|
||||
```yaml
|
||||
moderator:
|
||||
akismet_key: "your_key"
|
||||
```
|
||||
|
||||
## 腾讯云文本内容安全
|
||||
|
||||
可参考:[腾讯云文档](https://cloud.tencent.com/document/product/1124/64508)
|
||||
|
||||
开通「文本内容安全」后,在「访问管理」-「API 密钥管理」新增具有权限的 Secret,然后填入配置:
|
||||
|
||||
```yaml
|
||||
moderator:
|
||||
tencent:
|
||||
enabled: true
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
```
|
||||
|
||||
## 阿里云内容安全
|
||||
|
||||
可参考:[阿里云文档](https://help.aliyun.com/document_detail/28417.html)
|
||||
|
||||
开通「阿里云内容安全」后,阿里云后台创建 Access Key 并填入配置:
|
||||
|
||||
```yaml
|
||||
moderator:
|
||||
aliyun:
|
||||
enabled: true
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
```
|
||||
|
||||
## 关键词词库过滤
|
||||
|
||||
如果你不想依赖于远程 API,可以在本地配置导入词库文件,让 Artalk 根据词语来检测垃圾评论:
|
||||
|
||||
```yaml
|
||||
moderator:
|
||||
keywords:
|
||||
enabled: true
|
||||
pending: false # 匹配成功设为待审状态
|
||||
files: # 支持多个词库文件
|
||||
- "./data/词库_1.txt"
|
||||
file_sep: "\n" # 词库文件内容分割符
|
||||
replac_to: "x" # 替换字符
|
||||
```
|
||||
|
||||
- **pending**:当成功匹配时,是否将评论设为待审核状态。
|
||||
- **files**:词库文件。允许多个文件,Artalk 启动时会合并词库。
|
||||
- **file_sep**:词库文件内容分割符。例如:文件中每行一个词语,该项配置 `\n`。
|
||||
- **replac_to**:替换字符。例如:该项设置为 `x`,你可以将 `pending` 设置为 `false`,评论自动过审,但匹配到的词语会被替换为 `x`,例如 `fxxk`、`xxxx`。
|
||||
|
||||
注:`replac_to` 不建议使用 `*` 星号,应为它和 Markdown 的加粗语法冲突。
|
||||
|
||||
## 使用验证码
|
||||
|
||||
你可以开启 Artalk 的验证码功能,支持图片和滑动验证码,[参考此处](./captcha.md)。
|
||||
@@ -0,0 +1,160 @@
|
||||
# 管理员 × 多站点
|
||||
|
||||
Artalk 支持站点隔离,以及为站点分配指定的管理员账户,
|
||||
|
||||
所以,你可以实现两种模式:单人多站点、多人多站点。
|
||||
|
||||
运用「单人多站点」模式,你能够实现多个站点共用同一个 Artalk 后端程序,集中化管理。
|
||||
|
||||
运用「多人多站点」模式,你可以和你的朋友们共用同一个 Artalk 后端程序。😉
|
||||
|
||||
## 创建超级管理员
|
||||
|
||||
执行命令根据命令行提示快速创建超级管理员。
|
||||
|
||||
```sh
|
||||
./artalk admin
|
||||
```
|
||||
|
||||
如果使用 Docker 可以执行以下命令:
|
||||
|
||||
```sh
|
||||
docker exec -it artalk artalk admin
|
||||
```
|
||||
|
||||
之后可使用超级管理员账户登陆控制中心,在用户管理页面的图形界面可创建更多账户,而无需手动编辑配置文件。
|
||||
|
||||
## 站点的创建和管理
|
||||
|
||||
你可以在侧边栏「[控制中心](../frontend/sidebar.md#控制中心)」创建多个站点,管理站点和快速切换站点,详情可参考:[侧边栏](/guide/frontend/sidebar.html)。
|
||||
|
||||
## 管理员配置
|
||||
|
||||
你可以设置多个管理员账户,当输入框输入匹配管理员用户名和邮箱时,将弹出密码验证提示框,
|
||||
|
||||
并且只有管理员才能访问侧边栏「[控制中心](../frontend/sidebar.md#控制中心)」,在前端对评论内容进行管理操作。
|
||||
|
||||
在配置文件中添加如下内容:
|
||||
|
||||
```yaml
|
||||
admin_users:
|
||||
- name: "admin"
|
||||
email: "admin@example.com"
|
||||
password: ""
|
||||
badge_name: "管理员"
|
||||
badge_color: "#0083FF"
|
||||
- name: "admin2"
|
||||
email: "admin2@example.com"
|
||||
password: ""
|
||||
badge_name: "小管理员"
|
||||
badge_color: "#0083FF"
|
||||
```
|
||||
|
||||
每一项配置的解释:
|
||||
|
||||
- **name** & **email**:用户名和邮箱,**不区分大小写**。
|
||||
- **password**:用户密码。
|
||||
|
||||
支持 bcrypt 和 md5 加密,例如填写:`"(md5)50c21190c6e4e5418c6a90d2b5031119"`。
|
||||
|
||||
**建议使用更安全的 bcrypt 加密算法**,在 Linux 环境下,你可以使用 [htpasswd 命令](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) 来生成密文:
|
||||
|
||||
```bash
|
||||
unset HISTFILE # 临时禁用 history 防止密码在历史记录中出现
|
||||
htpasswd -bnBC 10 "" "your_password" | tr -d ':'
|
||||
```
|
||||
|
||||
然后配置填写:`"(bcrypt)$2y$10$ti4vZYIrxVN8rLcY..."`,以 `(bcrypt)` 开头。
|
||||
|
||||
命令解释参考:[Compute bcrypt hash from command line](https://unix.stackexchange.com/questions/307994/compute-bcrypt-hash-from-command-line#answer-419855)
|
||||
|
||||
- **badge_name**:用户显示的头衔徽标文字。
|
||||
- **badge_color**:用户显示的头衔徽标背景颜色。
|
||||
|
||||
## 超级管理员、普通管理员
|
||||
|
||||
管理员分为「超级管理员」和「普通管理员」,前者可以管理全部站点,后者只能管理部分站点。
|
||||
|
||||
普通管理员将:
|
||||
|
||||
- 不会收到其它站点的评论邮件通知,邮件通知彼此隔离。
|
||||
- 不能对设定范围外的站点进行 删除、修改、查询 等操作。
|
||||
- 仅能在受限模式下使用 数据导入导出、站点管理 等功能。
|
||||
|
||||
完整的 `admin_users` 配置如下:
|
||||
|
||||
```yaml
|
||||
admin_users:
|
||||
- name: "超级管理员"
|
||||
email: "super_admin@example.com"
|
||||
password: "(bcrypt)$2y$10$ti4vZYIrxVN8rLcYXVgXCO.GJND0dyI49r7IoF3xqIx8bBRmIBZRm"
|
||||
badge_name: "超级管理员"
|
||||
badge_color: "#0083ff"
|
||||
receive_email: false
|
||||
|
||||
- name: "普通管理员1"
|
||||
email: "admin1@example.com"
|
||||
password: "(bcrypt)$2y$10$ti4vZYIrxVN8rLcYXVgXCO.GJND0dyI49r7IoF3xqIx8bBRmIBZRm"
|
||||
badge_name: "管理员"
|
||||
badge_color: "#0083ff"
|
||||
sites:
|
||||
- 站点名 A
|
||||
- 站点名 B
|
||||
|
||||
- name: "普通管理员2"
|
||||
email: "admin2@example.com"
|
||||
password: "(bcrypt)$2y$10$ti4vZYIrxVN8rLcYXVgXCO.GJND0dyI49r7IoF3xqIx8bBRmIBZRm"
|
||||
badge_name: "管理员"
|
||||
badge_color: "#0083ff"
|
||||
sites:
|
||||
- 站点名 C
|
||||
- 站点名 D
|
||||
```
|
||||
|
||||
### 为管理员分配站点
|
||||
|
||||
- **sites**:向管理员分配站点,即控制管理员对于站点「[控制中心](../frontend/sidebar.md#控制中心)」的可访问权限。
|
||||
|
||||
当未分配站点时,即 sites 未配置时,该账户为「**超级管理员**」,可以管理全部站点:
|
||||
|
||||
```yaml
|
||||
admin_users:
|
||||
- name: "super_admin"
|
||||
# 未分配 sites,为超级管理员
|
||||
```
|
||||
|
||||
当分配站点后,即 sites 已配置时,该管理员成为「**普通管理员**」:
|
||||
|
||||
```yaml
|
||||
admin_users:
|
||||
- name: "a_admin_user"
|
||||
# ↓ 为账户分配 sites
|
||||
sites:
|
||||
- 站点名 A
|
||||
- 站点名 B
|
||||
```
|
||||
|
||||
注:你至少需要一个超级管理员,这样才能创建站点。
|
||||
|
||||
### 控制管理员接收邮件通知
|
||||
|
||||
当页面有新的评论时,邮件会发送给**该站点**的全部管理员,但你可以配置 `receive_email` 强制禁用它。
|
||||
|
||||
这对于设定了多个邮箱,但又不希望某些邮箱收到评论邮件的情况很有帮助。
|
||||
|
||||
- **receive_email**:设置为 `false` 系统将不会发送邮件通知给该用户。
|
||||
|
||||
注:禁止后该管理员用户仍可以收到来自他人的 @AT 回复,只是当用户对页面进行评论时 (创建根评论时) 不发送邮件通知。
|
||||
|
||||
```yaml
|
||||
admin_users:
|
||||
- name: "super_admin"
|
||||
# 未分配 sites,即为超级管理员
|
||||
receive_email: false # ← 超级管理员不接收邮件
|
||||
|
||||
- name: "a_admin_user"
|
||||
# 分配 sites,即为普通管理员
|
||||
sites:
|
||||
- 站点名 A
|
||||
- 站点名 B
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# 多元通知
|
||||
|
||||
Artalk v2.1.8+ 配置项:
|
||||
|
||||
- `notify` 弃用并变更为 `admin_notify`
|
||||
- `email.mail_subject_to_admin` 弃用并变更为 `admin_notify.email.mail_subject`
|
||||
|
||||
```yaml
|
||||
# 管理员多元推送
|
||||
admin_notify:
|
||||
# 通知模版
|
||||
notify_tpl: "default"
|
||||
noise_mode: false
|
||||
# 邮件通知管理员
|
||||
email:
|
||||
enabled: true # 当使用其他推送方式时,可以关闭管理员邮件通知
|
||||
mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复"
|
||||
```
|
||||
|
||||
|
||||
请参考新文档:[多元推送](./admin_notify.md)
|
||||
@@ -0,0 +1,53 @@
|
||||
# 相对 / 绝对路径
|
||||
|
||||
Artalk 支持解析相对路径,因此你可以在前端页面进行如下配置:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
site: "举个栗子站点", // 你的站点名
|
||||
pageKey: "/relative-path/xx.html", // 使用相对路径
|
||||
})
|
||||
```
|
||||
|
||||
建议页面使用相对路径,因为这为日后的「站点迁移」需求创建条件。
|
||||
|
||||
然后,你需要在侧边栏「[控制中心](../frontend/sidebar.md#控制中心)」-「站点」找到 “举个栗子站点”,修改站点 URL。
|
||||
|
||||

|
||||
|
||||
之后,所有相对路径都会「基于这个站点 URL」,例如:
|
||||
|
||||
```bash
|
||||
"/relative-path/xx.html"
|
||||
↓ 解析为
|
||||
"https://设定的举个栗子站点URL.xxx/relative-path/xx.html"
|
||||
```
|
||||
|
||||
### 解析后的 URL 用途
|
||||
|
||||
站点 URL + 页面 相对路径 将用于:
|
||||
|
||||
- **邮件通知**中的回复评论链接
|
||||
- **侧边栏**快速跳转到某条评论
|
||||
- **控制中心**页面管理打开页面
|
||||
- **获取页面标题**等信息时使用
|
||||
|
||||
### 配置多个站点 URL 的情况
|
||||
|
||||
你可能需要配置站点的多个 URL 来允许 Referer 和跨域。
|
||||
|
||||
「[控制中心](../frontend/sidebar.md#控制中心)」-「站点」修改站点 URL 支持为站点添加多个 URL,用英文逗号 `,` 分隔开每个 URL 即可。
|
||||
|
||||
**当站点存在多个 URL 时**,「相对路径」会基于多个 URL 中的「**第一个**」URL。
|
||||
|
||||
### 使用绝对路径的情况
|
||||
|
||||
区别于使用相对路径,你可以使用绝对路径,例如前端这样配置:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
pageKey: "https://your_domain.com/relative-path/xx.html", // 使用绝对路径
|
||||
})
|
||||
```
|
||||
|
||||
这时后端不会去解析该地址,邮件、侧边栏等地方都是直接使用 `pageKey` 这个绝对路径来定位页面。
|
||||
@@ -0,0 +1,121 @@
|
||||
# 反向代理
|
||||
|
||||
## Caddy
|
||||
|
||||
**/etc/caddy/Caddyfile**
|
||||
|
||||
```nginx
|
||||
artalk.your_domain.com {
|
||||
tls /etc/caddy/ssl/cert.pem /etc/caddy/ssl/cert.key
|
||||
|
||||
reverse_proxy http://localhost:23366 {
|
||||
header_up X-Forwarded-For {header.X-Forwarded-For}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
执行重载命令:
|
||||
|
||||
```sh
|
||||
sudo systemctl reload caddy
|
||||
```
|
||||
|
||||
## Nginx
|
||||
|
||||
假定:
|
||||
|
||||
- 你想绑定的域名是:`artalk.your_domain.com`
|
||||
- Artalk 本地地址:`http://localhost:23366`
|
||||
|
||||
以 Ubuntu 20.04 为例:
|
||||
|
||||
创建站点配置文件:
|
||||
|
||||
```bash
|
||||
sudo vim /etc/nginx/sites-available/artalk.your_domain.com
|
||||
```
|
||||
|
||||
编辑反向代理配置文件:
|
||||
|
||||
```nginx
|
||||
server
|
||||
{
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name artalk.your_domain.com;
|
||||
|
||||
location / {
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_pass http://localhost:23366/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
创建软链接启用站点:
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/artalk.your_domain.com /etc/nginx/sites-enabled/
|
||||
```
|
||||
|
||||
验证配置文件是否有效:
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
配置没有问题,重启 Nginx:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
配置前端:
|
||||
|
||||
```js
|
||||
Artalk.init({ server: "http://artalk.your_domain.com" })
|
||||
```
|
||||
|
||||
::: tip
|
||||
你还可以再套一层 CDN,然后加上 SSL
|
||||
|
||||
注意配置文件权限,以及反代目标 URL 可访问性
|
||||
|
||||
尤其是运行在 Docker 容器内的 artalk,注意检查 IP 和端口是否能够被 Nginx 正常访问
|
||||
:::
|
||||
|
||||
## Apache
|
||||
|
||||
需要启用反代模块 `mod_proxy.c`
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName your_domain.xxx
|
||||
ServerAlias
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{QUERY_STRING} transport=polling [NC]
|
||||
RewriteRule /(.*) http://localhost:23366/$1 [P]
|
||||
|
||||
<IfModule mod_proxy.c>
|
||||
ProxyRequests Off
|
||||
SSLProxyEngine on
|
||||
ProxyPass / http://localhost:23366/
|
||||
ProxyPassReverse / http://localhost:23366/
|
||||
</IfModule>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
## 宝塔面板
|
||||
|
||||
首先创建一个站点 (例如 `artalk.your_domain.com`),然后点击站点的「设置」:
|
||||
|
||||

|
||||
|
||||
打开「反向代理」选项卡,点击「添加反向代理」,「目标 URL」填写 `http://localhost:端口号`(端口号与 Artalk 端口对应),「发送域名」填写 `$host`,如图:
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# 后端升级
|
||||
|
||||
## 更新变动
|
||||
|
||||
突破性变动,例如更新需要修改原有配置等,请注意查看版本更新日志中的提示:[CHANGELOG.md](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md)
|
||||
|
||||
## 命令行一键升级
|
||||
|
||||
执行 `./artalk upgrade`
|
||||
|
||||
此操作会从自动从 GitHub Release 下载并升级程序,执行前需关闭 Artalk。
|
||||
|
||||
:::tip
|
||||
|
||||
执行 `./artalk upgrade -f` 携带参数 `-f` 执行强制更新。
|
||||
|
||||
:::
|
||||
|
||||
## Docker 升级
|
||||
|
||||
可参考:[Docker · 升级](./docker.md#升级)
|
||||
|
||||
## 普通方式
|
||||
|
||||
前往 [GitHub Release](https://github.com/ArtalkJS/Artalk/releases) 手动下载最新构建
|
||||
|
||||
替换掉旧版本文件即可。
|
||||
@@ -0,0 +1,39 @@
|
||||
# 🚀 案例展示
|
||||
|
||||
### 开源项目
|
||||
|
||||
- [Wiki.js](https://github.com/requarks/wiki/): A modern and powerful wiki app built on Node.js
|
||||
- [vuepress-theme-hope](https://github.com/vuepress-theme-hope/vuepress-theme-hope): A vuepress theme with tons of features
|
||||
- [hexo-theme-stellar](https://github.com/xaoxuu/hexo-theme-stellar): Elegant and powerful theme for Hexo
|
||||
- [FixIt Theme | Hugo](https://github.com/Lruihao/FixIt): A clean, elegant but advanced blog theme for Hugo
|
||||
- [hexo-theme-volantis](https://github.com/volantis-x/hexo-theme-volantis): A Wonderful Theme for Hexo
|
||||
- [hexo-theme-cards](https://github.com/ChrAlpha/hexo-theme-cards): Another Simple & Swift theme for Hexo
|
||||
- [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly): A Hexo Theme: Butterfly
|
||||
|
||||
### 博客
|
||||
|
||||
- [一蓑烟雨](https://easyf12.top/)
|
||||
- [二丫讲梵](https://wiki.eryajf.net)
|
||||
- [Alliot’s blog](https://www.iots.vip/)
|
||||
- [小康博客](https://www.antmoe.com/)
|
||||
- [程序员 Life](https://xuqilong.top)
|
||||
- [远方的灯塔](https://terwergreen.com)
|
||||
- [Bowenの破站](https://bowenyoung.cn/)
|
||||
- [杜老师说](https://dusays.com/)
|
||||
- [频率](https://pinlyu.com/)
|
||||
- [子舒](https://zburu.com/)
|
||||
- [叶开](https://xn--qpru0x.cn/)
|
||||
- [Thun888](https://blog.thun888.xyz/)
|
||||
- [青空之蓝](https://blog.ixk.me/)
|
||||
- [Monstx](https://blog.monsterx.cn/)
|
||||
- [QWQAQ](https://qwqaq.com/)
|
||||
|
||||
::: tip
|
||||
|
||||
此页面案例由团队成员整理...
|
||||
|
||||
如果希望 / 不希望你使用到 Artalk 的项目在此展示,欢迎在下方留言!
|
||||
|
||||
非常感谢你为 Artalk 社区做出的贡献 😘
|
||||
|
||||
:::
|
||||
@@ -0,0 +1,197 @@
|
||||
# 📦 程序部署
|
||||
|
||||
## Docker 部署
|
||||
|
||||
推荐使用 Docker 部署,需预先安装 [Docker 引擎](https://docs.docker.com/engine/install/),服务器执行命令创建容器:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
--restart=always \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
> 假设域名 `http://your_domain` 已正确添加 DNS 记录并指向你的服务器 IP
|
||||
|
||||
浏览器打开 `http://your_domain:8080` 将出现 Artalk 后台登陆界面。
|
||||
|
||||
执行命令创建管理员账户:
|
||||
|
||||
```bash
|
||||
docker exec -it artalk artalk admin
|
||||
```
|
||||
|
||||
在你的网站引入 Artalk 程序内嵌的的前端 CSS、JS 资源并初始化:
|
||||
|
||||
> 注:将 `http://your_domain:8080` 改为你的服务器域名,或使用 [公共 CDN 资源](#cdn-资源)。
|
||||
|
||||
```html
|
||||
<!-- CSS -->
|
||||
<link href="http://your_domain:8080/dist/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="http://your_domain:8080/dist/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments', // 绑定元素的 Selector
|
||||
pageKey: '/post/1', // 固定链接 (留空自动获取)
|
||||
pageTitle: '关于引入 Artalk 的这档子事', // 页面标题 (留空自动获取)
|
||||
server: 'http://your_domain:8080', // 后端地址
|
||||
site: 'Artalk 的博客', // 你的站点名
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
在评论框输入管理员的用户名和邮箱,控制台入口按钮将出现在评论框右下角位置。
|
||||
|
||||
在控制台,你可以根据喜好配置评论系统、[将评论迁移到 Artalk](./transfer.md)。
|
||||
|
||||
🥳 你已成功完成 Artalk 部署!
|
||||
|
||||
## 普通方式部署
|
||||
|
||||
1. 前往 [GitHub Release](https://github.com/ArtalkJS/Artalk/releases) 下载程序压缩包
|
||||
2. 提取压缩包:`tar -zxvf artalk_版本号_系统_架构.tar.gz`
|
||||
3. 运行程序 `./artalk server`
|
||||
4. 前端配置
|
||||
|
||||
```js
|
||||
Artalk.init({ server: "http://your_domain:23366" })
|
||||
```
|
||||
|
||||
**进一步操作:**
|
||||
|
||||
- [守护进程 (Systemd, Supervisor)](./backend/daemon.md)
|
||||
- [反向代理 (Caddy, Nginx, Apache)](./backend/reverse-proxy.md)
|
||||
|
||||
如果有需要,你还可以自行编译程序,参考:[后端构建](./backend/build.md)。
|
||||
|
||||
## Compose 部署
|
||||
|
||||
**compose.yaml**
|
||||
|
||||
```yaml
|
||||
version: "3.5"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
```
|
||||
|
||||
执行命令创建容器:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
::: details 一些 Docker Compose 常用命令
|
||||
|
||||
```bash
|
||||
docker-compose restart # 重启容器
|
||||
docker-compose stop # 暂停容器
|
||||
docker-compose down # 删除容器
|
||||
docker-compose pull # 更新镜像
|
||||
docker-compose exec artalk bash # 进入容器
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
可参考:[Docker 部署文档](./backend/docker.md)
|
||||
|
||||
## CDN 资源
|
||||
|
||||
::: tip Artalk 最新版本
|
||||
|
||||
当前 Artalk 前端最新版本号为: **:ArtalkVersion:**
|
||||
|
||||
若需升级前端,请将 URL 中的版本号数字部分替换即可。
|
||||
:::
|
||||
|
||||
Artalk 后端程序内嵌了前端 JS、CSS 文件,使用公共 CDN 资源请注意前后端版本的兼容性。
|
||||
|
||||
Artalk 静态资源通过上游 CDNJS 分发,以下镜像资源可供选择:
|
||||
|
||||
**CDNJS**
|
||||
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdnjs.cloudflare.com/ajax/libs/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**Staticfile CDN (国内)**
|
||||
|
||||
> https://cdn.staticfile.org/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdn.staticfile.org/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
::: details 查看更多
|
||||
|
||||
**BootCDN (国内)**
|
||||
|
||||
> https://cdn.bootcdn.net/ajax/libs/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://cdn.bootcdn.net/ajax/libs/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**75CDN (国内)**
|
||||
|
||||
> https://lib.baomitu.com/artalk/:ArtalkVersion:/Artalk.js
|
||||
>
|
||||
> https://lib.baomitu.com/artalk/:ArtalkVersion:/Artalk.css
|
||||
|
||||
**UNPKG**
|
||||
|
||||
> https://unpkg.com/artalk@:ArtalkVersion:/dist/Artalk.js
|
||||
>
|
||||
> https://unpkg.com/artalk@:ArtalkVersion:/dist/Artalk.css
|
||||
|
||||
**JS DELIVR**
|
||||
|
||||
> https://cdn.jsdelivr.net/npm/artalk@:ArtalkVersion:/dist/Artalk.js
|
||||
>
|
||||
> https://cdn.jsdelivr.net/npm/artalk@:ArtalkVersion:/dist/Artalk.css
|
||||
|
||||
:::
|
||||
|
||||
## Node 项目
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
```
|
||||
|
||||
引入到你的项目:
|
||||
|
||||
```js
|
||||
import 'artalk/dist/Artalk.css'
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.init({
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
可参考:
|
||||
|
||||
- [置入框架文档](./frontend/import-framework.md)
|
||||
- [置入博客文档](./frontend/import-blog.md)
|
||||
- [前端 API](../develop/fe-api.md)
|
||||
|
||||
## 数据导入
|
||||
|
||||
从其他评论系统导入数据:[数据迁移](./transfer.md)
|
||||
|
||||
## ArtalkLite
|
||||
|
||||
可选择精简版 [ArtalkLite](./frontend/artalk-lite.md):体积更小、更简约。
|
||||
|
||||
## 开发环境
|
||||
|
||||
可参考:[开发者指南](https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md)
|
||||
@@ -0,0 +1,42 @@
|
||||
# 😴 深入了解
|
||||
|
||||
::: warning
|
||||
目前文档仍在陆续完善中
|
||||
:::
|
||||
|
||||
|
||||
## 控制台(管理后台)
|
||||
|
||||
面向管理员用户,我们提供了 “控制台” 功能,通过控制台,你能对数据内容进行统一管理。
|
||||
|
||||
### 如何呼出控制台
|
||||
|
||||
控制台被集成到评论页中,您仅需:
|
||||
1. 在评论框输入管理员的名字与邮箱
|
||||
2. 密码验证框自动弹出,输入正确密码
|
||||
3. 控制台按钮将会在评论框右下角显示
|
||||
|
||||
【图示】
|
||||
|
||||
## 通知中心
|
||||
|
||||
通知中心入口按钮将会在用户填入名字和邮箱后显示,它在评论框的右下角,与管理员控制台按钮位置相同。
|
||||
|
||||
通知中心的评论分为 提及、我的、全部、待审 四类。
|
||||
|
||||
- 提及:被人 AT 或对你的回复
|
||||
- 我的:我发布的评论
|
||||
- 全部:我的所有评论及相关评论
|
||||
- 待审:我的待审核评论
|
||||
|
||||
## 邮件发送
|
||||
|
||||
Artalk 支持以多种方式发送邮件,
|
||||
|
||||
## 审核制度
|
||||
|
||||
你能够通过设置,开启 评论默认待审,当用户提交评论后,需要管理员审核后才能显示,在此期间仅评论者自己可见。
|
||||
|
||||
## 反垃圾
|
||||
|
||||
Artalk 提供多种反垃圾的方法,其具体实现我们称之为 垃圾评论检查器,一旦检测到垃圾评论,检查器会将它标记为 “待审核” 状态。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 📚 扩展阅读
|
||||
|
||||
“我们仰望同一片天空,却看着不同的地方。”
|
||||
|
||||
Artalk 离不开社区的支持与帮助,**是你们让 Artalk 走得更远!**
|
||||
|
||||
非常感谢对 Artalk 提供支持与帮助的朋友们!😘
|
||||
|
||||
## Artalk v2
|
||||
|
||||
- [Artalk 评论系统推荐 (By @inkss)](https://inkss.cn/blog/8f37d8c3/)
|
||||
- [Nuxt.js如何部署Artalk和遇到的问题 (By @子舒)](https://imhan.cn/posts/20220218/)
|
||||
- [宝塔安装过程大概记录 (By @xiamuguizhi)](https://github.com/ArtalkJS/Artalk/discussions/46)
|
||||
- [Vuepress配置评论插件为Artalk](https://wiki.eryajf.net/pages/b74c2b/)
|
||||
- [Fly.io 部署 Artalk 评论系统](https://wayjam.me/posts/artalk-deploy-on-flyio/)
|
||||
- [Railway.app 部署 Artalk 评论系统](https://blog.moraxyc.com/post/eidb8276/)
|
||||
- [Render.com 部署Artalk自托管评论系统](https://www.leenhawk.com/posts/3932123804/)
|
||||
|
||||
更多新版读物正在撰写当中...
|
||||
|
||||
## Artalk v1
|
||||
|
||||
::: warning
|
||||
|
||||
Artalk v1 版本基于 [PHP 后端](https://github.com/ArtalkJS/ArtalkPHP),部分内容可能不适用于 Go 后端,但仍具有参考价值。
|
||||
|
||||
:::
|
||||
|
||||
- [[!!!] 一个 Artalk 的非官方文档 (By @thun888)](https://blog.thun888.xyz/wiki/Artalk/)
|
||||
- [为 Gridsome 添加 Artalk 自托管评论系统 - Monstx's Blog](https://blog.monsterx.cn/code/use-self-hosted-comment-system-in-gridsome/)
|
||||
- [Artalk 自托管评论系统的后端部署 - Jalen's Blog](https://blog.jalenchuh.cn/posts/artalk-api-php/)
|
||||
- [无服务器搭建 Artalk 评论系统后端 - 陈YFの博客](https://blog.cyfan.top/p/480ab6ed.html)
|
||||
- [哔哩哔哩 [站内首发] Artalk 评论系统搭建教程 - @thun888](https://www.bilibili.com/s/video/BV1954y1E7uP)
|
||||
- [Hexo Next 主题添加 Artalk 评论系统 - 心底的河流](https://lhy.life/20201126-artalk-next/)
|
||||
- [Hexo 添加 Artalk 评论教程 - 我相信我可以](https://butterfly.imlete.cn/article/Hexo-Artalk.html)
|
||||
- [基于 Butterfly 主题添加 Artalk 评论系统 - 卓越科技的 Blog](https://blog.imzykj.cn/posts/93afb348/)
|
||||
|
||||
|
||||
## 衍生品
|
||||
|
||||
- [Artalk:一款 ~~简洁~~ 有趣的自托管评论系统 (By monsterxcn)](https://github.com/monsterxcn/Artalk)
|
||||
|
||||
## 终于更新了!
|
||||
|
||||
在 Artalk v1 向 v2 的跨越期间,我经历了高考,这真是一段超级漫长的时间呢。
|
||||
|
||||
高考后,时间变得相对充裕,Artalk 后端采用 Golang 完成了重置,同时 Artalk 的功能也在不断完善当中。 🥳
|
||||
|
||||
<p style="text-align: right;">@qwqcode</p>
|
||||
@@ -0,0 +1,13 @@
|
||||
# 精简版本
|
||||
|
||||
ArtalkLite 是 Artalk 的精简版本,相较于普通版,有以下区别:
|
||||
|
||||
- 体积更小,更为简约
|
||||
- 默认关闭表情包、投票、UA 徽标
|
||||
- 去除 marked 依赖,但支持一些基本的 Markdown 语法
|
||||
|
||||
ArtalkLite 随普通版一起发布,你可以将 CDN 地址文件名修改为 ArtalkLite 获取:
|
||||
|
||||
> https://unpkg.com/artalk@2/dist/ArtalkLite.js
|
||||
>
|
||||
> https://unpkg.com/artalk@2/dist/ArtalkLite.css
|
||||
@@ -0,0 +1,473 @@
|
||||
# 前端配置
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.update({ ... })
|
||||
```
|
||||
|
||||
- 默认配置:[defaults.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/defaults.ts)
|
||||
- 声明文件:[config.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/config.ts)
|
||||
|
||||
## 轻松配置
|
||||
|
||||
推荐在侧边栏 “[控制中心](/guide/frontend/sidebar.md)” 通过图形界面修改前端的配置,而无需在代码中对界面进行设定。
|
||||
|
||||
注:前端的设定可能会被后端覆盖,更多内容参考:[在后端控制前端](/guide/backend/fe-control.md)
|
||||
|
||||
## 基本配置(必填项)
|
||||
|
||||
### el
|
||||
|
||||
**装载元素**(填入需要绑定的元素 Selector)
|
||||
|
||||
- 类型:`String|HTMLElement`
|
||||
- 默认值:`undefined`
|
||||
|
||||
> 例如:`#Comments` 对应元素 `<div id="Comments"></div>`
|
||||
|
||||
### pageKey
|
||||
|
||||
**页面地址**(相对路径 / 完整 URL)
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`location.pathname`
|
||||
|
||||
可留空自动获取页面的相对路径。
|
||||
|
||||
可以填写由博客系统生成的 `固定链接`,但建议使用相对路径以便日后切换域名。
|
||||
|
||||
参考:[关于使用相对 / 绝对路径](/guide/backend/relative-path.md)
|
||||
|
||||
### pageTitle
|
||||
|
||||
**页面标题**(用于管理列表显示,邮件通知等)
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`document.title`
|
||||
|
||||
可留空自动获取页面的 `<title>` 标签值。
|
||||
|
||||
### server
|
||||
|
||||
**后端程序 API 地址**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`undefined`
|
||||
|
||||
部署后端,确保后端地址前端可访问
|
||||
|
||||
> 例如:http://yourdomain.xxx
|
||||
|
||||
::: warning 更新注意
|
||||
|
||||
v2.2.6+ 的后续版本,请填入不带 `/api/` 路径的后端 URL。
|
||||
|
||||
:::
|
||||
|
||||
### site
|
||||
|
||||
**站点名称**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`undefined`
|
||||
|
||||
可留空使用后端配置的 “默认站点”,
|
||||
|
||||
Artalk 支持多站点统一管理,此项用于站点隔离。
|
||||
|
||||
### useBackendConf
|
||||
|
||||
**跟随后端的配置**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`(默认启用)
|
||||
|
||||
可以在后端的配置文件中定义前端的配置,让前端配置始终跟随后端。
|
||||
|
||||
详情可参考:[在后端控制前端](/guide/backend/fe-control)
|
||||
|
||||
## 国际化 (i18n)
|
||||
|
||||
### locale
|
||||
|
||||
**语言**
|
||||
|
||||
- 类型:`String|Object|"auto"`
|
||||
- 默认值:`"zh-CN"`
|
||||
|
||||
遵循 Unicode BCP 47 规范,该项默认为 "zh-CN" (简体中文)。
|
||||
|
||||
欢迎提交 PR 帮助翻译 Artalk 的多语言,为社区贡献一份力量!
|
||||
|
||||
详情参考:[多语言](./i18n.md)
|
||||
|
||||
## 请求
|
||||
|
||||
### reqTimeout
|
||||
|
||||
**请求超时**
|
||||
|
||||
- 类型:`Number`
|
||||
- 默认值:`15000`
|
||||
|
||||
当请求时间大于该值,自动断开请求并报错。(单位:毫秒)
|
||||
|
||||
## 表情包
|
||||
|
||||
### emoticons
|
||||
|
||||
**表情包**
|
||||
|
||||
- 类型:`Object|Array|String|Boolean`
|
||||
- 默认值:"[https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json](https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json)"
|
||||
|
||||
详细内容:[前端 · 表情包](/guide/frontend/emoticons.md)
|
||||
|
||||
更新兼容 [OwO 格式](https://github.com/DIYgod/OwO),支持 URL 动态加载。
|
||||
|
||||
设置为 `false` 关闭表情包功能。
|
||||
|
||||
:::warning 请替换 CDN 资源
|
||||
JS DELIVR 在中国大陆的 [ICP 牌照已被吊销](https://github.com/jsdelivr/jsdelivr/issues/18348#issuecomment-997777996)。
|
||||
:::
|
||||
|
||||
## 界面
|
||||
|
||||
### placeholder
|
||||
|
||||
**评论框占位字符**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"键入内容..."`
|
||||
|
||||
### noComment
|
||||
|
||||
**评论为空时显示字符**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"「此时无声胜有声」"`
|
||||
|
||||
### sendBtn
|
||||
|
||||
**发送按钮文字**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"发送评论"`
|
||||
|
||||
### editorTravel
|
||||
|
||||
**评论框穿梭**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
设置为 `true` 当回复评论时,评论框移动到待回复评论位置之后,而不是固定不动。
|
||||
|
||||
### darkMode
|
||||
|
||||
**夜间模式**
|
||||
|
||||
- 类型:`Boolean|"auto"`
|
||||
- 默认值:`false`
|
||||
|
||||
当 Artalk 被 new 时会读取该值,并根据该值选择是否开启夜间模式(可与博客主题配合使用)。
|
||||
|
||||
代码动态修改 darkMode:
|
||||
|
||||
```js
|
||||
artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
> 参考代码:“[index.html](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/index.html#L97-L150)”
|
||||
|
||||
可设置为 `"auto"`,Artalk 将监听 `(prefers-color-scheme: dark)` 根据用户操作系统判断自动切换夜间模式。
|
||||
|
||||
### flatMode
|
||||
|
||||
**平铺模式**
|
||||
|
||||
- 类型:`Boolean|"auto"`
|
||||
- 默认值:`"auto"`
|
||||
|
||||
默认 `"auto"` 仅小尺寸屏幕设备自动开启「平铺」模式 (屏幕宽度 < 768px 时)
|
||||
|
||||
设置 `true` 评论以「平铺模式」形式显示
|
||||
|
||||
设置 `false` 评论以「层级嵌套」形式显示
|
||||
|
||||
### nestMax
|
||||
|
||||
**最大嵌套层数**
|
||||
|
||||
- 类型:`Number`
|
||||
- 默认值:`2`
|
||||
|
||||
评论「层级嵌套」模式的最大嵌套层数。
|
||||
|
||||
### nestSort
|
||||
|
||||
**嵌套评论的排序规则**
|
||||
|
||||
- 类型:`"DATE_ASC"|"DATE_DESC"|"VOTE_UP_DESC"`
|
||||
- 默认值:`"DATE_ASC"`
|
||||
|
||||
嵌套评论的子评论默认以「日期升序 (新评的论在末尾)」排列。
|
||||
|
||||
## 功能
|
||||
|
||||
### pvEl
|
||||
|
||||
**页面浏览量 (PV) 绑定元素**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"#ArtalkPV"`
|
||||
|
||||
你可以在页面任意位置,放置 HTML 标签:`<span id="ArtalkPV"></span>`
|
||||
|
||||
当 Artalk 完成加载时展示页面的浏览量。
|
||||
|
||||
该项填入绑定元素的 Selector,默认为 `#ArtalkPV`。
|
||||
|
||||
### countEl
|
||||
|
||||
**评论数绑定元素**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"#ArtalkCount"`
|
||||
|
||||
你可以在页面任意位置,放置 HTML 标签:`<span id="ArtalkCount"></span>` 显示当前页面的评论数。
|
||||
|
||||
::: tip
|
||||
|
||||
pvEl 和 countEl 元素标签都可以设置 `data-page-key` 属性值,来指定显示某个页面的统计数目,例如:`<span id="ArtalkCount" data-page-key="/t/1.html"></span>`
|
||||
|
||||
详情参考:[浏览量统计](./pv.md#显示多个页面的浏览量)
|
||||
|
||||
:::
|
||||
|
||||
### vote
|
||||
|
||||
**投票按钮**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
启用评论投票功能 (赞同 / 反对)。
|
||||
|
||||
### voteDown
|
||||
|
||||
**反对按钮**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
反对的投票按钮(默认隐藏)。
|
||||
|
||||
### uaBadge
|
||||
|
||||
**显示用户的 UserAgent 信息徽标**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
### listSort
|
||||
|
||||
**评论排序功能**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
鼠标移到评论列表左上角「n 条评论」的位置,显示悬浮下拉层,可以让评论列表按照「最新 / 最热 / 最早 / 作者」等规则排序筛选显示。
|
||||
|
||||
### imgUpload
|
||||
|
||||
**图片上传功能**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
该配置项自动跟随后端,当后端图片上传功能关闭时,仅管理员会显示图片上传按钮。
|
||||
|
||||
### imgUploader
|
||||
|
||||
**图片上传器**
|
||||
|
||||
- 类型:`(file: File) => Promise<string>`
|
||||
- 默认值:`undefined`
|
||||
|
||||
自定义图片上传器,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
imgUploader: async (file) => {
|
||||
const form = new FormData()
|
||||
form.set('file', file)
|
||||
|
||||
const imgUrl = await fetch("https://api.example.org/upload", {
|
||||
method: 'POST',
|
||||
body: form
|
||||
})
|
||||
|
||||
return imgUrl
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### preview
|
||||
|
||||
**编辑器实时预览功能**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
显示编辑器的「预览」按钮。
|
||||
|
||||
## 头像
|
||||
|
||||
```js
|
||||
gravatar: {
|
||||
mirror: '<Gravatar 镜像地址>',
|
||||
default: 'mp',
|
||||
}
|
||||
```
|
||||
|
||||
### gravatar.mirror
|
||||
|
||||
**Gravatar 镜像地址**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"https://sdn.geekzu.org/avatar/"`
|
||||
|
||||
如果你觉得 Gravatar 头像加载速度不理想,可以尝试替换。
|
||||
|
||||
例如:
|
||||
|
||||
> Cravatar:https://cravatar.cn/avatar/
|
||||
>
|
||||
> V2EX:https://cdn.v2ex.com/gravatar/
|
||||
>
|
||||
> 极客族:https://sdn.geekzu.org/avatar/
|
||||
>
|
||||
> loli:https://gravatar.loli.net/avatar/
|
||||
|
||||
### gravatar.params
|
||||
|
||||
**Gravatar API 参数**
|
||||
|
||||
- 类型:`String`
|
||||
- 默认值:`"d=mp&s=240"`
|
||||
|
||||
例如,你可以通过该配置项设置默认头像 (`d=mp`) 和头像尺寸 (`s=240`)。
|
||||
|
||||
参考:[Gravatar API 文档](http://cn.gravatar.org/site/implement/images/)
|
||||
|
||||
该配置项格式为 HTTP Query。
|
||||
|
||||
::: warning 更新注意
|
||||
|
||||
v2.5.5 已废弃 `gravatar.default` 配置项,请使用 `gravatar.params` 替代。
|
||||
|
||||
:::
|
||||
|
||||
### avatarURLBuilder
|
||||
|
||||
**头像链接生成器**
|
||||
|
||||
- 类型:`(comment: CommentData) => string`
|
||||
- 默认值:`undefined`
|
||||
|
||||
自定义用户头像图片链接生成,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
avatarURLBuilder: (comment) => {
|
||||
return `/api/avatar?email=${comment.email_encrypted}`
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 评论分页
|
||||
|
||||
```js
|
||||
pagination: {
|
||||
pageSize: 20, // 每页评论数
|
||||
readMore: true, // 加载更多 or 分页条
|
||||
autoLoad: true, // 自动加载 (加载更多)
|
||||
}
|
||||
```
|
||||
|
||||
### pagination.readMore
|
||||
|
||||
**加载更多模式**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
设置 `true` 为 “**加载更多**” 模式
|
||||
|
||||
设置 `false` 为 “**分页条**” 模式
|
||||
|
||||
### pagination.autoLoad
|
||||
|
||||
**滚动到底部自动加载**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认值:`true`
|
||||
|
||||
(需同时开启“加载更多”模式,将 `readMore` 设置为 `true`)
|
||||
|
||||
### pagination.pageSize
|
||||
|
||||
**每次请求获取数量**
|
||||
|
||||
- 类型:`Number`
|
||||
- 默认值:`20`
|
||||
|
||||
## 内容限高
|
||||
|
||||
超过设定高度的内容将被隐藏,并显示“阅读更多”按钮。
|
||||
|
||||
```js
|
||||
heightLimit: {
|
||||
content: 300, // 评论内容限高
|
||||
children: 400, // 子评论区域限高
|
||||
scrollable: false, // 限高滚动
|
||||
}
|
||||
```
|
||||
|
||||
### heightLimit.content
|
||||
|
||||
**评论内容限高**
|
||||
|
||||
- 类型:`Number`
|
||||
- 默认值:`300`
|
||||
|
||||
> 当值为 0 时,关闭限高
|
||||
|
||||
### heightLimit.children
|
||||
|
||||
**子评论区域限高**
|
||||
|
||||
- 类型:`Number`
|
||||
- 默认值:`400`
|
||||
|
||||
### heightLimit.scrollable
|
||||
|
||||
**限高区域滚动**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认:`false`
|
||||
|
||||
允许限高区域滚动。
|
||||
|
||||
## 版本检测
|
||||
|
||||
### versionCheck
|
||||
|
||||
**版本检测**
|
||||
|
||||
- 类型:`Boolean`
|
||||
- 默认:`true`
|
||||
|
||||
当前端和后端版本不兼容时,显示警告提示框。
|
||||
@@ -0,0 +1,171 @@
|
||||
# 表情包
|
||||
|
||||
编辑后端 `artalk.yml` 配置文件并修改配置项 `frontend.emoticons` 填入表情包列表 URL:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
emoticons: "https://raw.githubusercontent.com/ArtalkJS/Emoticons/master/grps/default.json"
|
||||
```
|
||||
|
||||
## 表情包预设
|
||||
|
||||
Artalk 社区提供许多表情包预设,你能够挑选几套喜欢的表情包,仅需简单的配置,轻松添加到你的评论系统中,前往仓库:[@ArtalkJS/Emoticons](https://github.com/ArtalkJS/Emoticons)。
|
||||
|
||||
## 格式支持
|
||||
|
||||
### OwO 格式
|
||||
|
||||
[OwO](https://github.com/DIYgod/OwO) 是作者 [@DIYgod](https://github.com/DIYgod) 开发的开源 JS 插件,能让输入框快速拥有插入表情符号的功能。
|
||||
|
||||
Artalk 的表情包功能借鉴了其优秀的设计,并且 Artalk 原生适配和兼容 OwO 格式的表情包数据文件,示例如下:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
emoticons: "https://raw.githubusercontent.com/DIYgod/OwO/master/demo/OwO.json"
|
||||
# 直接食用 OwO 格式的表情包 ↑↑
|
||||
```
|
||||
|
||||
社区也有许多可以直接食用的 OwO 格式表情包资源,例如:[@2X-ercha/Twikoo-Magic](https://github.com/2X-ercha/Twikoo-Magic)。
|
||||
|
||||
### Artalk 表情包列表文件标准格式
|
||||
|
||||
Artalk 除了支持 OwO 格式外,还内置支持一种标准的表情包列表文件格式:
|
||||
|
||||
```js
|
||||
[{
|
||||
"name": "颜表情",
|
||||
"type": "emoticon", // 字符类型
|
||||
"items": [
|
||||
{ "key": "Hi", "val": "|´・ω・)ノ" },
|
||||
{ "key": "开心", "val": "ヾ(≧∇≦*)ゝ" },
|
||||
//...
|
||||
]
|
||||
}, {
|
||||
"name": "滑稽",
|
||||
"type": "image", // 图片类型
|
||||
"items": [
|
||||
{
|
||||
"key": "原味稽",
|
||||
"val": "<图片 URL>"
|
||||
},
|
||||
//...
|
||||
]
|
||||
}]
|
||||
```
|
||||
|
||||
## 前端配置
|
||||
|
||||
::: warning
|
||||
|
||||
前端的配置将默认被后端覆盖,详情参考:[在后端控制前端](/guide/backend/fe-control.html)
|
||||
|
||||
:::
|
||||
|
||||
在前端配置表情包列表,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
// 默认表情包列表,动态引入 ↓↓
|
||||
emoticons: "https://raw.githubusercontent.com/ArtalkJS/Emoticons/master/grps/default.json",
|
||||
})
|
||||
```
|
||||
|
||||
## 关闭表情包
|
||||
|
||||
你可以将 `emoticons` 设置为 `false` 来禁用表情包功能:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
emoticons: false
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## 加载方式
|
||||
|
||||
### 动态加载
|
||||
|
||||
将 `emoticons` 属性设置为表情包数据文件的 URL,当打开表情包列表时,Artalk 会动态引入。
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
emoticons: "<表情包数据文件 URL>",
|
||||
})
|
||||
```
|
||||
|
||||
远程的表情包文件支持 Artalk、OwO 格式,且支持嵌套、混合加载。
|
||||
|
||||
### 静态加载
|
||||
|
||||
相较于动态引入,可以将表情包列表对象,作为 Artalk 配置,静态保存在页面的 JS 代码中,避免动态加载:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
emoticons: [{
|
||||
"name": "颜表情",
|
||||
"type": "emoticon", // 字符类型
|
||||
"items": [
|
||||
{ "key": "Hi", "val": "|´・ω・)ノ" },
|
||||
{ "key": "开心", "val": "ヾ(≧∇≦*)ゝ" },
|
||||
//...
|
||||
]
|
||||
}, {
|
||||
"name": "滑稽",
|
||||
"type": "image", // 图片类型
|
||||
"items": [
|
||||
{
|
||||
"key": "原味稽",
|
||||
"val": "<图片 URL>"
|
||||
},
|
||||
//...
|
||||
]
|
||||
}],
|
||||
})
|
||||
```
|
||||
|
||||
### 混合加载
|
||||
|
||||
Artalk 支持 **动态**、**静态** 混合加载,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
emoticons: [
|
||||
// 动态加载
|
||||
"https://raw.githubusercontent.com/DIYgod/OwO/master/demo/OwO.json", // OwO 格式表情包
|
||||
"https://raw.githubusercontent.com/qwqcode/huaji/master/huaji.json",
|
||||
// 静态加载
|
||||
{
|
||||
"name": "表情包名字",
|
||||
"type": "emoticon", // 字符类型
|
||||
"items": [
|
||||
{ "key": "去吧大师球", "val": "(╯°A°)╯︵○○○" },
|
||||
//...
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### 嵌套引入
|
||||
|
||||
Artalk 支持远程表情包资源中**嵌套引入**另外的表情包资源,例如:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
emoticons: [
|
||||
"https://example.org/表情包.json"
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
文件 `表情包.json` 中的数据:
|
||||
|
||||
```json
|
||||
[
|
||||
"https://example.org/其他表情包.json",
|
||||
//...
|
||||
{ // Artalk 格式、OwO 格式
|
||||
//...
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
# 多语言
|
||||
|
||||
在配置文件 `artalk.yml` 中设置:
|
||||
|
||||
```yaml
|
||||
locale: "zh-CN"
|
||||
```
|
||||
|
||||
locale 配置项格式遵循 Unicode BCP 47 规范,默认为 "zh-CN" (简体中文)。
|
||||
|
||||
---
|
||||
|
||||
目前 Artalk 支持语言如下:
|
||||
|
||||
- "zh-CN" (简体中文)
|
||||
- "en-US" (English)
|
||||
|
||||
## 贡献翻译
|
||||
|
||||
欢迎提交 PR 帮助翻译 Artalk 的多语言,为社区贡献一份力量!
|
||||
|
||||
|翻译内容|语言文件目录|模板文件|
|
||||
|-|-|-|
|
||||
|后端程序|[/i18n/[LANG].yml](https://github.com/ArtalkJS/Artalk/tree/master/i18n)|[zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/i18n/zh-CN.yml)|
|
||||
|前端界面|[/ui/packages/artalk/src/i18n/[LANG].ts](https://github.com/ArtalkJS/Artalk/tree/master/ui/packages/artalk/src/i18n)|[zh-CN.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/i18n/zh-CN.ts)|
|
||||
|配置文件|[/conf/artalk.example.[LANG].yml](https://github.com/ArtalkJS/Artalk/tree/master/conf)|[artalk.example.zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/conf/artalk.example.zh-CN.yml)
|
||||
|说明文档|[/docs/[LANG]/**\/*.md](https://github.com/ArtalkJS/Artalk/tree/master/docs)|[/docs/**\/*.md](https://github.com/ArtalkJS/Artalk/tree/master/docs)
|
||||
|
||||
更多参考:[开发文档](../../develop/index.md) / [CONTRIBUTING.md](https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md#translation)
|
||||
|
||||
## 进阶配置
|
||||
|
||||
### 前端临时修改语言
|
||||
|
||||
前端会默认使用后端的语言配置,但你也可以在前端修改配置项 `locale` 来设定语言:
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
locale: 'en-US'
|
||||
})
|
||||
```
|
||||
|
||||
### 前端语言自动切换
|
||||
|
||||
可将前端 locale 配置为 `"auto"`,根据用户浏览器自动切换语言,当语言不存在时,将被设置为 "en-US"。
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
locale: "auto"
|
||||
})
|
||||
```
|
||||
|
||||
### 前端自定义 locale 内容
|
||||
|
||||
在前端可传入 Object 类型,按照 [模板文件](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/src/i18n/zh-CN.ts) 中的 Keys 编写自定义 locale 内容。
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
locale: {
|
||||
placeholder: 'こんにちは',
|
||||
//...
|
||||
}
|
||||
})
|
||||
```
|
||||
Loaded 100 of 730 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user