Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9c3b2a1c3 | ||
|
|
bd121f8f94 | ||
|
|
6b0e46c86c | ||
|
|
53aaf27fdf | ||
|
|
a3d1ccc773 | ||
|
|
cf7725edba | ||
|
|
9cc3252c0f | ||
|
|
349eb6f5c5 | ||
|
|
55a96f12aa | ||
|
|
36580b5fc3 | ||
|
|
9afa7ca555 | ||
|
|
6d54b721da | ||
|
|
cb4596d59d | ||
|
|
4913e1aebd | ||
|
|
7ffaa04541 | ||
|
|
2a49c0e515 | ||
|
|
a98a50abbe | ||
|
|
4bd3537c0d | ||
|
|
9dcfd9cd91 | ||
|
|
9fde40d843 | ||
|
|
19c3317f64 | ||
|
|
22ff47f9ce | ||
|
|
4eb3dbaca2 | ||
|
|
90d8503d0e | ||
|
|
2354f7aa06 | ||
|
|
7bab69c4fd | ||
|
|
301928da4b | ||
|
|
6135c7e911 | ||
|
|
1ff0c4c10f | ||
|
|
9cc9a6d6ab | ||
|
|
d2225d565b | ||
|
|
e61911027c | ||
|
|
f29d2f81eb | ||
|
|
4b5ec39b8e | ||
|
|
5dc2752cc1 | ||
|
|
cf6e687a2d | ||
|
|
69ea7de22e | ||
|
|
8fb7027186 | ||
|
|
88615f9a88 | ||
|
|
f104629b40 | ||
|
|
2f6b30deaf | ||
|
|
2f7b9fa816 | ||
|
|
6d1041dc69 | ||
|
|
9fb191976c | ||
|
|
f20454bd00 | ||
|
|
64854107ef | ||
|
|
ac54eae179 | ||
|
|
aa92a92961 | ||
|
|
d54652264b | ||
|
|
d6ececd014 | ||
|
|
04ef002da1 | ||
|
|
a6b559d682 | ||
|
|
67b37b4612 | ||
|
|
3e81b2a8f2 | ||
|
|
8a9480d05a | ||
|
|
b3ed639be7 | ||
|
|
6a0e06ea75 | ||
|
|
92484cfa66 | ||
|
|
1b0eff9568 | ||
|
|
2efd34248d | ||
|
|
c0a3026890 | ||
|
|
392b6b15cf | ||
|
|
6b58fd8a9d | ||
|
|
80e5ca5dc7 | ||
|
|
50e967f71c | ||
|
|
cf58a545b5 | ||
|
|
270558129f | ||
|
|
059bac8b5f | ||
|
|
f1a86d59ca | ||
|
|
adcb75a2e2 | ||
|
|
4d7e157ff0 | ||
|
|
528847de7b | ||
|
|
2b9d6bb573 | ||
|
|
393224cdb1 | ||
|
|
a925806d29 | ||
|
|
b97167a718 | ||
|
|
587523f0df | ||
|
|
08f58cd38b | ||
|
|
0803e235a4 | ||
|
|
125a66611f | ||
|
|
92a0e23c85 | ||
|
|
c75b630e8a | ||
|
|
d22d34c223 | ||
|
|
6fe8ffd2c4 | ||
|
|
f4d33fcf24 | ||
|
|
640f9904fb | ||
|
|
df62de09f3 | ||
|
|
68bcdbe3e8 | ||
|
|
e663893fd1 | ||
|
|
b85d845b71 | ||
|
|
acf7480b91 | ||
|
|
be0dcd6b59 | ||
|
|
4d9445c84c | ||
|
|
1337e81cd4 | ||
|
|
d053b953a6 | ||
|
|
2549052558 | ||
|
|
b8da04e308 | ||
|
|
c4a7162302 | ||
|
|
6012993fe9 | ||
|
|
d4ed7f64b2 | ||
|
|
93e3b2f184 | ||
|
|
0e75457499 | ||
|
|
b8151bb789 | ||
|
|
67922f34ad | ||
|
|
7093e7e1c5 | ||
|
|
c3558b7beb | ||
|
|
5fdf3a27ce | ||
|
|
f8f509c0de | ||
|
|
437d943691 | ||
|
|
401cf5c21a | ||
|
|
77e9a687ac | ||
|
|
85f88cbba5 | ||
|
|
efb34c23e5 | ||
|
|
822a85c9e3 | ||
|
|
630a3b1ff8 | ||
|
|
ca6f8ed09c | ||
|
|
a0b0a33862 | ||
|
|
4868cd9992 | ||
|
|
2dfee533da | ||
|
|
dcaaefc71b | ||
|
|
499a57213e | ||
|
|
b7fc86b3c6 | ||
|
|
161ff8bfc3 | ||
|
|
1e36d45ec3 | ||
|
|
d2161cc0fb | ||
|
|
abbddfe9b4 | ||
|
|
860dd698e7 | ||
|
|
acb087887c | ||
|
|
dbb3c55148 | ||
|
|
fdfeef8042 | ||
|
|
824f122dcb | ||
|
|
53d46d82b6 | ||
|
|
bb218d0ba4 | ||
|
|
984071d4b6 | ||
|
|
34b24a1f36 | ||
|
|
58657524cf | ||
|
|
b5db161697 | ||
|
|
4dd0ae40e2 | ||
|
|
5dea08a3f2 | ||
|
|
92464085e6 | ||
|
|
28f1fa11bd | ||
|
|
3de1f11f47 | ||
|
|
dff1813fa5 | ||
|
|
f4475830da | ||
|
|
9c85a85153 | ||
|
|
5a63d4c63d | ||
|
|
b8afcbb21a | ||
|
|
0c0a207068 | ||
|
|
be939816aa | ||
|
|
45d1bc1241 | ||
|
|
ef5899e079 | ||
|
|
7187d0f058 | ||
|
|
88aec7e767 | ||
|
|
a09628c4e8 | ||
|
|
23d0d868de | ||
|
|
f3e356e450 | ||
|
|
170db0fc2a | ||
|
|
c3f730b0e7 | ||
|
|
e132c31150 | ||
|
|
fa6648ecf5 | ||
|
|
a9331b426e | ||
|
|
fbd1cd984d | ||
|
|
accd10cef4 | ||
|
|
690eb47eaa | ||
|
|
fc0dcee06c | ||
|
|
895bfbd5e2 | ||
|
|
e254350211 | ||
|
|
f226d476e5 | ||
|
|
ee131fe6ef | ||
|
|
43e4c901b8 | ||
|
|
bd5da20dd6 | ||
|
|
116d8f1919 | ||
|
|
99b71a15f8 | ||
|
|
3255cb4b66 | ||
|
|
35d5f778b2 | ||
|
|
9375ef812a | ||
|
|
44742c149e | ||
|
|
a4d5135379 | ||
|
|
ddfaa603ca | ||
|
|
6e13152fc1 | ||
|
|
0b16c9627a | ||
|
|
b613a4368c | ||
|
|
3098a75a05 | ||
|
|
d71ac6d156 | ||
|
|
6c9a7e3884 | ||
|
|
65b440416d | ||
|
|
f9084cbfb4 | ||
|
|
3c7ad1d0ef | ||
|
|
1930d842f2 | ||
|
|
751e4a866f | ||
|
|
01364d1ddc | ||
|
|
ec6d2b2e60 | ||
|
|
915b5f48b4 | ||
|
|
16527ec4f3 | ||
|
|
a7af2b7aaa | ||
|
|
3ff891be90 | ||
|
|
7d6faf6431 | ||
|
|
73d8ea97ed | ||
|
|
c482e19f19 | ||
|
|
8c3042cdb1 | ||
|
|
ed9a78472d | ||
|
|
ae5543df52 | ||
|
|
c2c5a80d8d | ||
|
|
de5e1b4000 | ||
|
|
b60cb585b6 | ||
|
|
44bca8d2f4 | ||
|
|
9103a923a6 | ||
|
|
e4242512f8 | ||
|
|
0ebd16a5db | ||
|
|
0eb75f612a | ||
|
|
18e6ae5b17 | ||
|
|
1ee3c41199 | ||
|
|
8af713636c | ||
|
|
5bf6e5f1dc | ||
|
|
929faf0722 | ||
|
|
5322f90383 | ||
|
|
fa3575cbae | ||
|
|
c94a34ffd7 | ||
|
|
50b49448b6 | ||
|
|
8ff3f5e875 | ||
|
|
640fa7ded4 | ||
|
|
18123f7002 | ||
|
|
cf2094667d | ||
|
|
2cd2165f06 | ||
|
|
4eada4b628 | ||
|
|
05f3ad043a | ||
|
|
6e9083e75d | ||
|
|
7bbdb5900f | ||
|
|
5be45315dc | ||
|
|
5d20ff1ef5 | ||
|
|
86e0eeafd1 | ||
|
|
49c1a87a67 | ||
|
|
ec7f81cb83 | ||
|
|
4b115d7b6a | ||
|
|
1bae6d8d2a | ||
|
|
bdafa19470 | ||
|
|
342236b6a7 | ||
|
|
ffce9f9e85 | ||
|
|
0097e041b1 | ||
|
|
42da621dfc | ||
|
|
2f5dbaa022 | ||
|
|
e181a6d5dd | ||
|
|
7873b21fb7 | ||
|
|
2a219518a4 | ||
|
|
afa7888db0 | ||
|
|
f6a589d5b2 | ||
|
|
41a09d5996 | ||
|
|
191977c0df | ||
|
|
5a4c06daee | ||
|
|
c4942ab807 | ||
|
|
6b83a9f632 | ||
|
|
3c88c0c29c | ||
|
|
35aab6bedc | ||
|
|
17033dc111 | ||
|
|
559df60f0d | ||
|
|
22d4ec1c31 | ||
|
|
d97ad5c843 | ||
|
|
34a67f647b | ||
|
|
9c3228b8fc | ||
|
|
4012d3e090 | ||
|
|
bc1c4d315c | ||
|
|
2a6f0c56a4 | ||
|
|
46e2a15116 | ||
|
|
c068f5a240 | ||
|
|
29bf49958f | ||
|
|
2fcad539ca | ||
|
|
45b79124c9 | ||
|
|
2b44452234 | ||
|
|
0e0b53fe9e | ||
|
|
dba5ba5277 | ||
|
|
942aaf654f | ||
|
|
37e0091502 | ||
|
|
a7c3f1bbda | ||
|
|
a40a070bdb | ||
|
|
082bfc63d1 | ||
|
|
0de8009e65 | ||
|
|
a5e4c81e72 | ||
|
|
c52c26145c | ||
|
|
f5e310a182 | ||
|
|
0828b91602 | ||
|
|
c4f664e412 | ||
|
|
a084c50095 | ||
|
|
9dacd0954d | ||
|
|
50b0a6068c | ||
|
|
4689492bf5 | ||
|
|
6ee9ee9ea4 | ||
|
|
8e3a743ed8 | ||
|
|
9550a16a0c | ||
|
|
a9edac3ce0 | ||
|
|
589850d311 | ||
|
|
9ba9439831 | ||
|
|
38f6b6996a | ||
|
|
1e16570b2f | ||
|
|
889adc6bae | ||
|
|
0de59911b3 | ||
|
|
f22e557b1c | ||
|
|
92ff757efc | ||
|
|
63165599ce | ||
|
|
a5b428d1ec | ||
|
|
cf3f9b1ac3 | ||
|
|
c212d67f49 | ||
|
|
b00a257699 | ||
|
|
3698c0c593 | ||
|
|
32a398609c | ||
|
|
54729fbafc | ||
|
|
b74e5cf94c | ||
|
|
475dd933fe | ||
|
|
60e0e7e61a | ||
|
|
e4a42d3a90 | ||
|
|
d86cd73ea6 | ||
|
|
67280f2ca2 | ||
|
|
a8bcd41f36 | ||
|
|
04cf137a74 | ||
|
|
d542e98581 | ||
|
|
dfaf95bf1d | ||
|
|
0a89426c93 | ||
|
|
7a026ef0e2 | ||
|
|
855f6c261f | ||
|
|
7099598a92 | ||
|
|
ad8851ea94 | ||
|
|
def9dc2707 | ||
|
|
91228a9446 | ||
|
|
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 |
No files matched your search
@@ -1,7 +1,9 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
node: circleci/node@5.0.3
|
||||
node: circleci/node@6.1.0
|
||||
go: circleci/go@1.11.0
|
||||
codecov: codecov/codecov@4.1.0
|
||||
|
||||
executors:
|
||||
base:
|
||||
@@ -9,18 +11,15 @@ executors:
|
||||
- image: cimg/base:stable
|
||||
node:
|
||||
docker:
|
||||
- image: 'cimg/node:16.15.1'
|
||||
- image: "cimg/node:20.17.0"
|
||||
go:
|
||||
docker:
|
||||
- image: cimg/go:1.20
|
||||
- image: cimg/go:1.22.7
|
||||
|
||||
parameters:
|
||||
trigger-app:
|
||||
type: boolean
|
||||
default: false
|
||||
trigger-ui:
|
||||
type: boolean
|
||||
default: false
|
||||
run-them-all:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -28,59 +27,33 @@ parameters:
|
||||
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.20-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: go mod download
|
||||
- save_cache:
|
||||
key: go-mod-1.20-{{ 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
|
||||
- store_test_results:
|
||||
path: /tmp/test-reports
|
||||
|
||||
## Frontend
|
||||
test_ui:
|
||||
executor: node
|
||||
steps:
|
||||
- node/install
|
||||
- checkout
|
||||
- run:
|
||||
name: Print the node version
|
||||
command: node -v
|
||||
- run:
|
||||
name: Install pnpm
|
||||
command: npm i -g pnpm@7.25.0
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm --dir ./ui install --frozen-lockfile
|
||||
- run:
|
||||
name: Build Artalk
|
||||
command: pnpm --dir ./ui build:all
|
||||
executor: go
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Print the Go version"
|
||||
command: go version
|
||||
- go/load-cache
|
||||
- go/mod-download
|
||||
- go/save-cache
|
||||
- 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"
|
||||
executor: base
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
echo "all"
|
||||
|
||||
workflows:
|
||||
build-app:
|
||||
@@ -91,14 +64,6 @@ workflows:
|
||||
jobs:
|
||||
- test_app
|
||||
|
||||
build-ui:
|
||||
when:
|
||||
or:
|
||||
- << pipeline.parameters.trigger-ui >>
|
||||
- << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
- test_ui
|
||||
|
||||
build-shared-other:
|
||||
when: << pipeline.parameters.run-them-all >>
|
||||
jobs:
|
||||
|
||||
@@ -8,7 +8,7 @@ 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@0.0.1
|
||||
path-filtering: circleci/path-filtering@1.0.0
|
||||
|
||||
workflows:
|
||||
setup:
|
||||
@@ -17,10 +17,11 @@ workflows:
|
||||
mapping: |
|
||||
(cmd|internal|server|pkg|test|conf)/.* trigger-app true
|
||||
main.go|go.mod|go.sum trigger-app true
|
||||
(ui)/.* trigger-ui true
|
||||
base-revision: master # git branch name
|
||||
base-revision: master # git branch name
|
||||
config-path: .circleci/conditional_config.yml
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages
|
||||
- /release\/.+/
|
||||
- nightly
|
||||
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
+3
-2
@@ -2,7 +2,8 @@
|
||||
|
||||
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
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
buy_me_a_coffee: artalk
|
||||
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
|
||||
@@ -11,4 +12,4 @@ 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
|
||||
- https://afdian.com/a/artalk
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Comment issue
|
||||
description: Create comment on a issue
|
||||
|
||||
inputs:
|
||||
issue_number:
|
||||
required: true
|
||||
description: The issue number you want to comment
|
||||
content:
|
||||
required: true
|
||||
description: The content of comment
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
env:
|
||||
STDOUT: ${{ inputs.content }}
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: ${{ inputs.issue_number }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: process.env.STDOUT
|
||||
})
|
||||
@@ -18,4 +18,4 @@
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
@@ -4,8 +4,8 @@ info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://github.com/ArtalkJS/Artalk
|
||||
options:
|
||||
tag_filter_pattern: '^v'
|
||||
sort: "date"
|
||||
tag_filter_pattern: "^v"
|
||||
sort: date
|
||||
commits:
|
||||
filters:
|
||||
Type:
|
||||
@@ -13,16 +13,26 @@ options:
|
||||
- fix
|
||||
- perf
|
||||
- refactor
|
||||
- test
|
||||
- docs
|
||||
commit_groups:
|
||||
sort_by: Custom
|
||||
title_order:
|
||||
- feat
|
||||
- fix
|
||||
- perf
|
||||
- refactor
|
||||
- test
|
||||
- docs
|
||||
title_maps:
|
||||
feat: Features
|
||||
fix: Bug Fixes
|
||||
perf: Performance Improvements
|
||||
refactor: Code Refactoring
|
||||
test: Testing
|
||||
docs: Documentation
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s\\/]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
@@ -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
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": ["config:base", "schedule:weekly"],
|
||||
"automerge": false,
|
||||
"major": {
|
||||
"automerge": false
|
||||
},
|
||||
"branchPrefix": "chore/",
|
||||
"labels": ["dependencies"],
|
||||
"gomodTidy": true,
|
||||
"rangeStrategy": "bump",
|
||||
"reviewers": []
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
name: Build App
|
||||
run-name: Build Go App and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.20.1'
|
||||
PKG_NAME: 'github.com/ArtalkJS/Artalk'
|
||||
GO_VERSION: 1.22.7
|
||||
DOCKER_IMG: ghcr.io/goreleaser/goreleaser-cross
|
||||
CACHE_DIR: /tmp/cache/docker-image
|
||||
|
||||
@@ -17,50 +21,54 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.version }}
|
||||
# `go-releaser` will read the semver version from git tag name
|
||||
# `git-chglog` will diff the tag and generate changelog
|
||||
fetch-depth: 0
|
||||
ref: master
|
||||
|
||||
- name: Docker Image Cache
|
||||
id: docker-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.CACHE_DIR }}
|
||||
key: docker-image-go-cross-${{ env.GO_VERSION }}
|
||||
# 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'
|
||||
# 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
|
||||
# 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
|
||||
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: |-
|
||||
# get latest version by tag
|
||||
VERSION=$(git describe --tags --abbrev=0)
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
mkdir -p local
|
||||
|
||||
# github token
|
||||
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" > local/.release-env
|
||||
|
||||
# changelog
|
||||
mkdir -p local
|
||||
git-chglog ${VERSION} > local/release-notes.md
|
||||
git-chglog --config .github/chglog/config.yml ${{ inputs.version }} > local/release-notes.md
|
||||
|
||||
# copy config file
|
||||
cp conf/artalk.example.yml artalk.yml
|
||||
|
||||
# github token
|
||||
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" > local/.release-env
|
||||
- name: Print Relase Notes
|
||||
run: cat local/release-notes.md
|
||||
|
||||
- name: Build and Release
|
||||
run: |
|
||||
@@ -69,9 +77,8 @@ jobs:
|
||||
--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 \
|
||||
-v $(pwd):/repo \
|
||||
-w /repo \
|
||||
--env-file local/.release-env \
|
||||
ghcr.io/goreleaser/goreleaser-cross:v${GO_VERSION} \
|
||||
release --release-notes local/release-notes.md
|
||||
release --release-notes local/release-notes.md ${{ inputs.dry_run && '--skip=publish' || '' }}
|
||||
@@ -1,9 +1,17 @@
|
||||
name: Build Docker
|
||||
run-name: Build Docker Image and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
DOCKER_IMG: artalk/artalk-go
|
||||
@@ -14,65 +22,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Checkout latest release 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
|
||||
uses: actions/checkout@v4
|
||||
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-
|
||||
|
||||
# TODO: https://github.com/docker/metadata-action/pull/248
|
||||
- name: Get Docker Tags and Labels
|
||||
run: |
|
||||
TAGS="${DOCKER_IMG}:latest,${DOCKER_IMG}:${VERSION#v},${DOCKER_IMG}:${VERSION_SHORT},${DOCKER_IMG}:sha-${COMMIT_HASH}"
|
||||
echo "DOCKER_TAGS=${TAGS}" >> $GITHUB_ENV
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Gen docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
context: git
|
||||
images: |
|
||||
${{ env.DOCKER_IMG }}
|
||||
tags: |
|
||||
@@ -82,35 +41,42 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: ${{ inputs.target }}
|
||||
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Build UI outside of Docker to speed up cross-platform builds
|
||||
- name: Build UI
|
||||
run: |
|
||||
make build-frontend
|
||||
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and Push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v3
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: ./
|
||||
push: ${{ !inputs.dry_run }}
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: 'linux/amd64,linux/arm64'
|
||||
push: true
|
||||
# tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: ${{ env.DOCKER_TAGS }}
|
||||
build-args: |
|
||||
APP_VERSION=${{ inputs.version }}
|
||||
SKIP_UI_BUILD=true
|
||||
platforms: ${{ inputs.target }}
|
||||
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 }}
|
||||
run: echo ${{ steps.build.outputs.digest }}
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Build Done
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
issue_number:
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
done:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
- name: Comment done info
|
||||
uses: ./.github/actions/comment
|
||||
if: ${{ inputs.issue_number != 0 }}
|
||||
with:
|
||||
issue_number: ${{ inputs.issue_number }}
|
||||
content: |-
|
||||
🎉 Finished build and publish ${{ inputs.version }} ([NPM Package](https://www.npmjs.com/package/artalk?activeTab=versions) / [Docker Image](https://hub.docker.com/r/artalk/artalk-go/tags) / [Github Release](https://github.com/ArtalkJS/Artalk/releases/tag/${{ inputs.version }})).
|
||||
@@ -1,54 +0,0 @@
|
||||
name: Build Frontend
|
||||
run-name: Build Frontend and Publish
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [artalk-release]
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
# set for building frontend
|
||||
working-directory: ./ui
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.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,361 @@
|
||||
name: Build Nightly
|
||||
run-name: Build Nightly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 10 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: "Dry run"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
env:
|
||||
DIST_DIR: /tmp/builds
|
||||
DIGEST_DIR: /tmp/digests
|
||||
DOCKER_IMG: artalk/artalk-go
|
||||
DOCKER_BUILD_ARGS: |-
|
||||
SKIP_UI_BUILD=true
|
||||
|
||||
jobs:
|
||||
# Prepare check should run
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
skip: ${{ steps.check.outputs.skip }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check should run
|
||||
id: check
|
||||
if: github.event_name == 'schedule'
|
||||
run: |
|
||||
if [[ "$(git log --since='24 hours ago' | wc -l)" -eq 0 ]] || \
|
||||
[[ "$GITHUB_REPOSITORY" != "ArtalkJS/Artalk" ]]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Bulid UI first
|
||||
# (build ui outside of docker to speed up cross-platform builds)
|
||||
ui:
|
||||
if: ${{ needs.prepare.outputs.skip != 'true' }}
|
||||
needs: prepare
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.10.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.PNPM_STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build UI
|
||||
run: |
|
||||
make build-frontend
|
||||
|
||||
- name: Pack UI
|
||||
id: pack
|
||||
run: |
|
||||
SRC_FILE=$(pnpm pack -C ui/artalk --pack-destination ../.. | tail -n 1)
|
||||
|
||||
mkdir -p bin
|
||||
PKG_FILE="bin/artalk_frontend_nigthly_$(date +'%Y%m%d').tar.gz"
|
||||
|
||||
mv $SRC_FILE $PKG_FILE
|
||||
echo "pkg_file=$PKG_FILE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload UI
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-src
|
||||
path: |
|
||||
public
|
||||
ui/artalk/dist
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload UI package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-ui-pkg
|
||||
path: ${{ steps.pack.outputs.pkg_file }}
|
||||
retention-days: 1
|
||||
|
||||
#
|
||||
# Build app on different platforms
|
||||
#
|
||||
build:
|
||||
needs: ui
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# - macos-14
|
||||
# :(
|
||||
# native Apple m1 arm64 is not supported nested virtualization yet, so docker qemu is not working.
|
||||
# https://github.com/actions/runner-images/issues/9460
|
||||
# https://docs.veertu.com/anka/anka-virtualization-cli/nested-virtualization/
|
||||
#
|
||||
# wait for arm64 linux runner support on github actions.
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
# exclude:
|
||||
# # exclude build x86 on arm, and vice versa.
|
||||
# - { os: macos-14, platform: linux/amd64 }
|
||||
# - { os: ubuntu-latest, platform: linux/arm64 }
|
||||
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
PF="${{ matrix.platform }}"
|
||||
echo "PLATFORM_PAIR=${PF//\//_}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup docker on macos
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
uses: crazy-max/ghaction-setup-docker@v3
|
||||
|
||||
- name: Gen docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.DOCKER_IMG }}
|
||||
tags: type=raw,value=nightly
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Download UI before build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ui-src
|
||||
|
||||
- name: Build and push by digest
|
||||
uses: docker/build-push-action@v6
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: ${{ env.DOCKER_BUILD_ARGS }}
|
||||
# The `type=image,push=true` must the last one,
|
||||
# because the `build.outputs.digest` will output the last image's digest.
|
||||
# The digests will be used in the merge step.
|
||||
outputs: |
|
||||
type=docker,name=${{ env.DOCKER_IMG }}
|
||||
type=image,push=true,name=${{ env.DOCKER_IMG }},push-by-digest=true,name-canonical=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p $DIGEST_DIR
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "$DIGEST_DIR/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ env.DIGEST_DIR }}/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
# Export docker image
|
||||
- name: Export Docker Image
|
||||
id: docker
|
||||
run: |
|
||||
IMG_FILE="artalk_docker_nigthly_$(date +'%Y%m%d')_${PLATFORM_PAIR}.tar.gz"
|
||||
docker save ${{ env.DOCKER_IMG }} | gzip > $IMG_FILE
|
||||
echo "img_file=$IMG_FILE" >> $GITHUB_OUTPUT
|
||||
|
||||
# App
|
||||
- name: Pack app
|
||||
id: app
|
||||
run: |
|
||||
BASE_DIR="/tmp/app_pkg"
|
||||
NAME="artalk_nigthly_$(date +'%Y%m%d')_${PLATFORM_PAIR}"
|
||||
SUB_DIR="$BASE_DIR/$NAME"
|
||||
PKG_FILE="$BASE_DIR/$NAME.tar.gz"
|
||||
|
||||
# bin file
|
||||
mkdir -p $SUB_DIR
|
||||
docker run --rm --entrypoint cat ${{ env.DOCKER_IMG }} /artalk > "$SUB_DIR/artalk"
|
||||
chmod +x "$SUB_DIR/artalk"
|
||||
|
||||
# doc file
|
||||
cp conf/artalk.example.yml "$SUB_DIR/artalk.yml"
|
||||
cp README.md LICENSE CHANGELOG.md "$SUB_DIR"
|
||||
|
||||
# create package file
|
||||
tar -czf $PKG_FILE -C $BASE_DIR $NAME
|
||||
echo "app_pkg_file=$PKG_FILE" >> $GITHUB_OUTPUT
|
||||
|
||||
# Move build artifacts
|
||||
- name: Move build artifacts
|
||||
run: |
|
||||
mkdir -p $DIST_DIR
|
||||
mv ${{ steps.app.outputs.app_pkg_file }} $DIST_DIR
|
||||
mv ${{ steps.docker.outputs.img_file }} $DIST_DIR
|
||||
|
||||
# Upload build artifacts
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ env.DIST_DIR }}/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
# Merge docker digests from different platforms
|
||||
# Reference: https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
|
||||
docker_merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DIGEST_DIR }}
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.DOCKER_IMG }}
|
||||
tags: type=raw,value=nightly
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ env.DIGEST_DIR }}
|
||||
run: |
|
||||
TAGS_ARG=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||||
MINIFEST_SRCS=$(printf '${{ env.DOCKER_IMG }}@sha256:%s ' *)
|
||||
|
||||
docker buildx imagetools create ${TAGS_ARG} ${MINIFEST_SRCS}
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.DOCKER_IMG }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
# Release
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download builds
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ env.DIST_DIR }}
|
||||
pattern: build-*
|
||||
merge-multiple: true
|
||||
|
||||
# Generate CHANGELOG
|
||||
- name: Generate Changelog
|
||||
run: |
|
||||
# install git-chglog
|
||||
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
|
||||
|
||||
CHANGELOG=$(git-chglog --config .github/chglog/config.yml --next-tag nightly nightly)
|
||||
MD_FILE="/tmp/changelog.md"
|
||||
echo -e "$CHANGELOG" > $MD_FILE
|
||||
echo -e "\n> [!NOTE]\n" \
|
||||
"> The Nightly version, automatically built based on the latest code, " \
|
||||
"is not yet released and may be unstable. Please use it with caution. " \
|
||||
"Docker user can run \`docker pull artalk/artalk-go:nightly\` to get " \
|
||||
"the nightly build." >> $MD_FILE
|
||||
|
||||
# checksums.txt
|
||||
- name: Calculate checksums.txt
|
||||
run: |
|
||||
cd $DIST_DIR
|
||||
sha256sum * > checksums.txt
|
||||
|
||||
# print checksums.txt
|
||||
cat checksums.txt
|
||||
|
||||
FILES=$(find ${DIST_DIR} -type f -exec readlink -f {} \;)
|
||||
echo -e "RELEASE_FILES<<EOF" >> $GITHUB_ENV
|
||||
echo -e "$FILES" >> $GITHUB_ENV
|
||||
echo -e "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Create tag and push
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
|
||||
gh release delete nightly --yes || true
|
||||
git push origin :refs/tags/nightly || true
|
||||
|
||||
git tag -f nightly
|
||||
git push -f origin nightly
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: true
|
||||
tag_name: nightly
|
||||
name: 🧪 Nightly Version
|
||||
body_path: /tmp/changelog.md
|
||||
files: ${{ env.RELEASE_FILES }}
|
||||
draft: false
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Build Pre Tagging
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
issue_number:
|
||||
required: true
|
||||
type: number
|
||||
outputs:
|
||||
version:
|
||||
description: "Version"
|
||||
value: ${{ jobs.tagging.outputs.version }}
|
||||
|
||||
jobs:
|
||||
tagging:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get release version
|
||||
run: |
|
||||
# `github.head_ref` only exists for pull_request events
|
||||
REF="${{ github.head_ref || github.ref }}"
|
||||
VERSION="${REF#refs/tags/}"
|
||||
VERSION="${VERSION#refs/heads/}"
|
||||
VERSION="${VERSION#release/}"
|
||||
|
||||
echo "Version: $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Invalid version format: $VERSION, please check git ref or tag."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Create Tag
|
||||
run: |
|
||||
if [ ! $(git tag -l "${VERSION}") ]; then
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
git tag -a $VERSION -m "Release $VERSION"
|
||||
git push origin $VERSION
|
||||
else
|
||||
echo "Skip creating tag $VERSION, already exists."
|
||||
fi
|
||||
|
||||
- name: Comment tag info to PR
|
||||
uses: ./.github/actions/comment
|
||||
if: ${{ inputs.issue_number != 0 }}
|
||||
with:
|
||||
issue_number: ${{ inputs.issue_number }}
|
||||
content: |-
|
||||
🔖 Tagged version [${{ env.VERSION }}](https://github.com/ArtalkJS/Artalk/tree/${{ env.VERSION }}) and updated the [CHANGELOG.md](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md).
|
||||
outputs:
|
||||
version: ${{ env.VERSION }}
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Build UI
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.10.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Publish
|
||||
run: pnpm publish -F artalk --no-git-checks ${{ inputs.dry_run && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
# GitHub Packages
|
||||
#
|
||||
# Setup .npmrc file to publish to GitHub Packages
|
||||
# - uses: actions/setup-node@v4
|
||||
# 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,82 @@
|
||||
name: Build and Publish
|
||||
run-name: Build and Publish (${{ github.head_ref || github.ref }}) ${{ inputs.dry_run && '(🧪 Dry-Run)' || '' }}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
types: [closed]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: "Dry run"
|
||||
type: boolean
|
||||
default: true
|
||||
build_items:
|
||||
description: "Build items"
|
||||
type: string
|
||||
default: "ui,app,docker"
|
||||
|
||||
jobs:
|
||||
# Tag the release version code before building
|
||||
# Getting the version code from the branch name or git ref.
|
||||
tagging:
|
||||
# Only run on manual trigger
|
||||
# or when a PR is merged into master.
|
||||
# (which the branch name start with `relase/`)
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/'))
|
||||
uses: ./.github/workflows/build-tagging.yml
|
||||
with:
|
||||
issue_number: ${{ github.event.pull_request.number || 0 }}
|
||||
dry_run: ${{ inputs.dry_run || false }}
|
||||
secrets: inherit
|
||||
|
||||
# Build the UI
|
||||
ui:
|
||||
if: inputs.build_items == '' || contains(inputs.build_items, 'ui')
|
||||
needs: tagging
|
||||
uses: ./.github/workflows/build-ui.yml
|
||||
with:
|
||||
version: ${{ needs.tagging.outputs.version }}
|
||||
dry_run: ${{ inputs.dry_run || false }}
|
||||
secrets: inherit
|
||||
|
||||
# Build the Docker image
|
||||
docker:
|
||||
if: inputs.build_items == '' || contains(inputs.build_items, 'docker')
|
||||
needs: tagging
|
||||
uses: ./.github/workflows/build-docker.yml
|
||||
# Parallel build the multi-arch docker image
|
||||
strategy:
|
||||
matrix:
|
||||
# since docker push actions will override the previous images,
|
||||
# so need to build `linux/amd64` and push first,
|
||||
# then build `linux/amd64` twice with other archs (build very slow so keep it last).
|
||||
target: ["linux/amd64", "linux/amd64,linux/arm64"]
|
||||
with:
|
||||
version: ${{ needs.tagging.outputs.version }}
|
||||
dry_run: ${{ inputs.dry_run || false }}
|
||||
target: ${{ matrix.target }}
|
||||
secrets: inherit
|
||||
|
||||
# Build the binary app
|
||||
app:
|
||||
if: inputs.build_items == '' || contains(inputs.build_items, 'app')
|
||||
needs: tagging
|
||||
uses: ./.github/workflows/build-app.yml
|
||||
with:
|
||||
version: ${{ needs.tagging.outputs.version }}
|
||||
dry_run: ${{ inputs.dry_run || false }}
|
||||
secrets: inherit
|
||||
|
||||
# All builds are complete
|
||||
done:
|
||||
needs: [tagging, ui, docker, app]
|
||||
uses: ./.github/workflows/build-done.yml
|
||||
with:
|
||||
version: ${{ needs.tagging.outputs.version }}
|
||||
issue_number: ${{ github.event.pull_request.number || 0 }}
|
||||
dry_run: ${{ inputs.dry_run || false }}
|
||||
secrets: inherit
|
||||
@@ -9,16 +9,18 @@
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '40 17 * * 4'
|
||||
- cron: "40 17 * * 4"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -32,43 +34,44 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
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
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 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.
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
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
|
||||
# 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@v3
|
||||
|
||||
# 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
|
||||
|
||||
# ℹ️ 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.
|
||||
|
||||
# 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
|
||||
|
||||
# - 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}}"
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -2,61 +2,41 @@ name: Docs CN Mirror Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "docs/**"
|
||||
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@v4
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.10.0
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: pnpm
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Build Docs
|
||||
run: pnpm build:docs
|
||||
|
||||
- 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
|
||||
- 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: docs/docs/.vitepress/dist/
|
||||
remote_path: /
|
||||
clean: true
|
||||
@@ -1,5 +1,5 @@
|
||||
name: 📦 Release New Version
|
||||
run-name: 📦 Release ${{ inputs.custom || inputs.semver }} by @${{ github.actor }}
|
||||
name: 0x📦 Release New Version
|
||||
run-name: 📦 Release ${{ inputs.semver }} by @${{ github.actor }} ${{ inputs.dry_run && '(🧪 Dry-Run)' || '' }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -12,82 +12,88 @@ on:
|
||||
- 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'
|
||||
custom_version:
|
||||
description: Manual Custom Version (Special Purpose, v*)
|
||||
type: string
|
||||
required: false
|
||||
dry_run:
|
||||
description: "Dry run?"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 20.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Setup semver
|
||||
run: npm install -g semver
|
||||
|
||||
- name: Handle Version Number
|
||||
run: |
|
||||
if [ ! -z "${{ inputs.custom }}" ]; then
|
||||
next_version="${{ inputs.custom }}"
|
||||
PREV_VERSION="$(git describe --tags --abbrev=0 --match 'v*')"
|
||||
|
||||
if [ -n "${{ inputs.custom_version }}" ]; then
|
||||
NEXT_VERSION="${{ inputs.custom_version }}"
|
||||
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})"
|
||||
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 "PREV_VERSION=${PREV_VERSION}" >> $GITHUB_ENV
|
||||
echo "VERSION=${NEXT_VERSION}" >> $GITHUB_ENV
|
||||
|
||||
echo "next_version: ${next_version}"
|
||||
echo "VERSION=${next_version}" >> $GITHUB_ENV
|
||||
- name: Print Next Version
|
||||
run: echo "${VERSION}"
|
||||
|
||||
- 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
|
||||
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
|
||||
- name: Version Changelog
|
||||
run: |
|
||||
if [ ! $(git tag -l "${VERSION}") ]; then
|
||||
# prepend changelog
|
||||
changelog=$(git-chglog --next-tag "${VERSION}" "${VERSION}")
|
||||
echo -e "${changelog}\n" | cat - CHANGELOG.md > temp && mv temp CHANGELOG.md
|
||||
# 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 for `internal/config/version.go`
|
||||
perl -i -pe 's/const Version = ".*?"/const Version = "'${VERSION}'"/g' internal/config/version.go
|
||||
|
||||
# modify the version in docs
|
||||
perl -pi -e 's#"latest"(\W+)?:(\W+)?".*?"#"latest": "'${VERSION#v}'"#g' docs/code/ArtalkVersion.json
|
||||
# modify the version for `package.json`
|
||||
perl -i -pe 's/"version": ".*?"/"version": "'${VERSION#v}'"/g' ui/artalk/package.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 CHANGELOG.md "${UI_NPM_PKG_FILE}"
|
||||
git commit -m "chore: release ${VERSION}"
|
||||
git tag "${VERSION}"
|
||||
# modify the version in docs
|
||||
perl -pi -e 's#"latest"(\W+)?:(\W+)?".*?"#"latest": "'${VERSION#v}'"#g' docs/docs/code/ArtalkVersion.json
|
||||
|
||||
git push origin master "${VERSION}"
|
||||
else
|
||||
echo "skipped because git tag already exists."
|
||||
fi
|
||||
- name: Print Git Diff
|
||||
run: git diff
|
||||
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
if: ${{ !inputs.dry_run }}
|
||||
with:
|
||||
event-type: ${{ env.REPO_DISPATCH_EVENT_TYPE }}
|
||||
branch: "release/${{ env.VERSION }}"
|
||||
commit-message: "chore: release ${{ env.VERSION }}"
|
||||
title: "chore: release ${{ env.VERSION }}"
|
||||
labels: release
|
||||
body: |
|
||||
## Release ${{ env.VERSION }} 📦🚀
|
||||
|
||||
This PR is auto-generated, please check the changelog and confirm the release. 😀
|
||||
|
||||
The build workflow will be triggered after the PR is merged.
|
||||
committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||
assignees: ${{ github.actor }}
|
||||
@@ -8,13 +8,13 @@ on:
|
||||
type: string
|
||||
description: Event Type
|
||||
required: true
|
||||
default: ''
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
event-type: ${{ inputs.event_type }}
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Test Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*", "!release/*", "!nightly"]
|
||||
paths: ["docs/**"]
|
||||
tags-ignore: ["v*"]
|
||||
pull_request:
|
||||
paths: ["docs/**"]
|
||||
branches: ["*", "!release/*", "!nightly"]
|
||||
|
||||
jobs:
|
||||
test_docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.10.0
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build Docs
|
||||
run: pnpm build:docs
|
||||
|
||||
- name: Lint Docs
|
||||
run: pnpm eslint docs && pnpm prettier --check docs
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Test Frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*", "!release/*", "!nightly"]
|
||||
paths: ["ui/**"]
|
||||
tags-ignore: ["v*"]
|
||||
pull_request:
|
||||
paths: ["ui/**"]
|
||||
branches: ["*", "!release/*", "!nightly"]
|
||||
|
||||
jobs:
|
||||
test_ui:
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Set git to use LF (for Windows environment)
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.10.0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Package Build Test
|
||||
run: pnpm build:all
|
||||
|
||||
- name: Unit test
|
||||
run: pnpm test
|
||||
|
||||
- name: Lint
|
||||
run: pnpm eslint ui && pnpm prettier --check ui
|
||||
|
||||
# https://github.com/arethetypeswrong/arethetypeswrong.github.io
|
||||
- name: "arethetypeswrong test"
|
||||
if: runner.os == 'ubuntu-latest'
|
||||
run: pnpm -F artalk test:attw || true
|
||||
@@ -48,6 +48,7 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
__debug*
|
||||
|
||||
# frontend
|
||||
node_modules
|
||||
@@ -64,3 +65,6 @@ node_modules
|
||||
# always compile latest version
|
||||
/public/sidebar
|
||||
/public/dist
|
||||
|
||||
playwright-report
|
||||
test-results
|
||||
+8
-8
@@ -14,9 +14,9 @@ env:
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# install dependencies
|
||||
- make install
|
||||
- make build-frontend
|
||||
# install dependencies
|
||||
- make install
|
||||
- make build-frontend
|
||||
|
||||
# build multi-platform
|
||||
builds:
|
||||
@@ -36,8 +36,6 @@ builds:
|
||||
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)
|
||||
@@ -60,7 +58,7 @@ builds:
|
||||
goarch:
|
||||
- arm
|
||||
goarm:
|
||||
- '7'
|
||||
- "7"
|
||||
env:
|
||||
- CC=arm-linux-gnueabihf-gcc
|
||||
- CXX=arm-linux-gnueabihf-g++
|
||||
@@ -118,7 +116,7 @@ builds:
|
||||
# https://go-review.googlesource.com/c/go/+/224588/
|
||||
# https://github.com/ArtalkJS/Artalk/issues/35
|
||||
&win_common_flags |
|
||||
-tags=timetzdata
|
||||
-tags=timetzdata
|
||||
|
||||
# Win (arm_64)
|
||||
- id: windows-arm64
|
||||
@@ -156,7 +154,7 @@ archives:
|
||||
- artalk.yml
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
name_template: "checksums.txt"
|
||||
|
||||
snapshot:
|
||||
name_template: "{{.Version}}-SNAPSHOT-{{.ShortCommit}}"
|
||||
@@ -175,3 +173,5 @@ release:
|
||||
name: Artalk
|
||||
prerelease: auto
|
||||
draft: false
|
||||
extra_files:
|
||||
- glob: ./local/release_includes/*
|
||||
File renamed without changes.
@@ -0,0 +1,20 @@
|
||||
CHANGELOG.md
|
||||
CHANGELOG.tpl.md
|
||||
CDN.html
|
||||
internal/template/notify_tpl/default.html
|
||||
|
||||
# generated files
|
||||
# @link https://github.com/prettier/prettier/issues/3634
|
||||
pnpm-lock.yaml
|
||||
**/auto-imports.d.ts
|
||||
**/components.d.ts
|
||||
**/typed-router.d.ts
|
||||
ui/artalk/src/api/v2.ts
|
||||
docs/swagger/swagger.json
|
||||
docs/swagger/swagger.yaml
|
||||
docs/docs/**/*.md
|
||||
docs/docs/.vitepress/dist/**
|
||||
docs/docs/.vitepress/cache/**
|
||||
ui/artalk-sidebar/src/lib/md5.js
|
||||
ui/artalk/src/lib/detect.ts
|
||||
**/dist/**
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"bracketSameLine": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.yaml", "*.yml"],
|
||||
"options": {
|
||||
"singleQuote": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+17
-13
@@ -1,14 +1,18 @@
|
||||
{
|
||||
"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}/local/local.yml"]
|
||||
}
|
||||
],
|
||||
}
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Artalk for Debugging",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/bin/artalk",
|
||||
"preLaunchTask": "Build for debugging",
|
||||
"env": {
|
||||
"ATK_SITE_DEFAULT": "ArtalkDocs",
|
||||
"ATK_TRUSTED_DOMAINS": "http://localhost:5173 http://localhost:23367"
|
||||
},
|
||||
"args": ["server", "-c", "${workspaceFolder}/artalk.yml"]
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+62
-18
@@ -1,19 +1,63 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"akismet",
|
||||
"artalk",
|
||||
"artran",
|
||||
"artrans",
|
||||
"artransfer",
|
||||
"gomail",
|
||||
"Goreleaser",
|
||||
"gorm",
|
||||
"ldflags",
|
||||
"lfshook",
|
||||
"Metas",
|
||||
"pgsql",
|
||||
"sendmail",
|
||||
"sqlite",
|
||||
"pkged"
|
||||
]
|
||||
}
|
||||
// Enable the ESlint flat config support
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
|
||||
// Disable the default formatter, use eslint instead
|
||||
// "prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
"eslint.runtime": "node",
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off" },
|
||||
{ "rule": "*-indent", "severity": "off" },
|
||||
{ "rule": "*-spacing", "severity": "off" },
|
||||
{ "rule": "*-spaces", "severity": "off" },
|
||||
{ "rule": "*-order", "severity": "off" },
|
||||
{ "rule": "*-dangle", "severity": "off" },
|
||||
{ "rule": "*-newline", "severity": "off" },
|
||||
{ "rule": "*quotes", "severity": "off" },
|
||||
{ "rule": "*semi", "severity": "off" }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml"
|
||||
],
|
||||
|
||||
"cSpell.words": [
|
||||
"akismet",
|
||||
"artalk",
|
||||
"artran",
|
||||
"artrans",
|
||||
"artransfer",
|
||||
"gomail",
|
||||
"Goreleaser",
|
||||
"gorm",
|
||||
"ldflags",
|
||||
"lfshook",
|
||||
"Metas",
|
||||
"pgsql",
|
||||
"sendmail",
|
||||
"sqlite",
|
||||
"pkged"
|
||||
]
|
||||
}
|
||||
Vendored
+12
-12
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build for debugging",
|
||||
"type": "shell",
|
||||
"command": "make build-debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build for debugging",
|
||||
"type": "shell",
|
||||
"command": "make build-debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="./docs/artalk.png" alt="Artalk" width="100%">
|
||||
</p>
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
[](https://github.com/ArtalkJS/Artalk/actions/workflows/dockerhub.yml) [](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_shield)
|
||||
[](https://hub.docker.com/r/artalk/artalk-go)
|
||||
|
||||
> Artalk: Golang backend of Artalk.
|
||||
|
||||
前往:[“**官方文档 · 后端部分**”](https://artalk.js.org/guide/backend/config.html)
|
||||
|
||||
---
|
||||
|
||||
- 高效快速
|
||||
- 异步执行
|
||||
- 跨平台兼容
|
||||
- 轻量级部署
|
||||
|
||||
## Supports
|
||||
|
||||
- 运行环境:支持 Linux, Windows, Darwin (x64 + ARM)
|
||||
- 数据存储:支持 SQLite, MySQL, PostgreSQL, SQL Server
|
||||
- 邮件发送:支持 SMTP, 阿里云邮件, 调用 sendmail 发送邮件
|
||||
- 高效缓存:支持 Redis, Memcache, In-Memory (BigCache)
|
||||
|
||||
## Build
|
||||
|
||||
> 部署可以使用官方提供的 Docker 镜像,见:[后端部署文档](https://artalk.js.org/guide/backend/install.html)
|
||||
|
||||
### 编译二进制文件
|
||||
|
||||
```sh
|
||||
$ make all
|
||||
```
|
||||
|
||||
编译后二进制文件将输出到 `bin/` 目录下
|
||||
|
||||
### Docker Compose 编译运行
|
||||
|
||||
```sh
|
||||
# 克隆项目
|
||||
$ git clone https://github.com/ArtalkJS/Artalk
|
||||
$ cd Artalk
|
||||
|
||||
# 构建镜像
|
||||
$ docker compose build
|
||||
|
||||
# 运行
|
||||
$ docker compose up -d
|
||||
```
|
||||
|
||||
### Docker 镜像构建
|
||||
|
||||
```sh
|
||||
# 克隆项目
|
||||
$ git clone https://github.com/ArtalkJS/Artalk
|
||||
$ cd Artalk
|
||||
|
||||
# 构建镜像
|
||||
$ make docker-build
|
||||
|
||||
# 发布镜像
|
||||
$ make docker-push
|
||||
```
|
||||
|
||||
## TODOs
|
||||
|
||||
Reference to https://github.com/ArtalkJS/Artalk#todos
|
||||
|
||||
## License
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_large)
|
||||
+759
-240
File diff suppressed because it is too large.
Load diff
+11
-11
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
||||
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,
|
||||
- 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
|
||||
- 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
|
||||
- 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,
|
||||
- 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
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
@@ -59,7 +59,7 @@ 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.
|
||||
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
|
||||
|
||||
+258
-59
@@ -1,99 +1,298 @@
|
||||
# Developer Contributing Guide
|
||||
|
||||
This guide is for developers who want to contribute to the project.
|
||||
This guide helps developers understand how to contribute code, documentation, tests, and feedback to the Artalk project. We welcome all forms of contributions, whether it's large feature development, small fixes, documentation improvements, or testing. We believe that every contribution is an important part of moving the project forward.
|
||||
|
||||
## Development Environment
|
||||
This guide is available in multiple languages: [English](https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md) • [简体中文](https://artalk.js.org/develop/contributing.html)
|
||||
|
||||
To develop Artalk, including the frontend and backend, you need to install the following tools:
|
||||
## Setup
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) (>= 16.0.0)
|
||||
- [Go](https://golang.org/) (>= 1.20)
|
||||
- [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:
|
||||
Clone the repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/ArtalkJS/Artalk
|
||||
git clone https://github.com/ArtalkJS/Artalk.git
|
||||
```
|
||||
|
||||
It is recommended to fork the repository first, and then clone the forked repository.
|
||||
It's recommended to fork the repository first, and then clone the forked repository.
|
||||
|
||||
Enter the directory:
|
||||
Navigate to the directory:
|
||||
|
||||
```sh
|
||||
cd Artalk
|
||||
```
|
||||
|
||||
### Build frontend and backend
|
||||
## Development Environment
|
||||
|
||||
First, we need to install the dependencies for backend written in Go. Simply run the following command:
|
||||
To develop Artalk, both frontend and backend, install the following tools:
|
||||
|
||||
```sh
|
||||
make build-debug
|
||||
```
|
||||
- [Node.js](https://nodejs.org/en/) (>= 20.17.0)
|
||||
- [PNPM](https://pnpm.io/) (>= 9.10.0)
|
||||
- [Go](https://golang.org/) (>= 1.22)
|
||||
- [Docker](https://www.docker.com/) (>= 20.10.0) (optional)
|
||||
- [Docker Compose](https://docs.docker.com/compose/) (>= 1.29.0) (optional)
|
||||
|
||||
This will build both the frontend and backend, with debugging symbols.
|
||||
## Development Workflow Overview
|
||||
|
||||
- **Frontend** will be built under `./ui/packages/artalk` and copied to `./public` directory.
|
||||
- **Backend** will be built under `./bin` directory.
|
||||
The development workflow consists of the following steps:
|
||||
|
||||
### Optional: Use one-key script to run a demo site
|
||||
1. **Set Up Complete Development Instance**:
|
||||
|
||||
If you want to run a demo site, you can use the following command:
|
||||
- Navigate to the Artalk Repo directory.
|
||||
- Execute `make dev` to run the backend on port `23366`.
|
||||
- Execute `pnpm dev` to run the frontend on port `5173`.
|
||||
- Optionally, execute `pnpm dev:sidebar` to run the sidebar frontend on port `23367`.
|
||||
|
||||
```sh
|
||||
./scripts/setup-example-site.sh
|
||||
```
|
||||
2. **Access Frontend for Development**:
|
||||
|
||||
This script sets up a local example site for testing at `local` folder, with Artalk integrated into its theme.
|
||||
- Open your browser and go to `http://localhost:5173` to perform your development and testing.
|
||||
|
||||
After running this script, run:
|
||||
3. **Testing**:
|
||||
|
||||
```sh
|
||||
./bin/artalk server -c ./local/local.yml
|
||||
```
|
||||
- Run the backend unit tests using `make test`.
|
||||
- Run the ui unit tests using `pnpm test`.
|
||||
- Run the ui E2E tests using `make test-frontend-e2e`.
|
||||
|
||||
to start the artalk server.
|
||||
4. **Building Frontend Code**:
|
||||
|
||||
And open <http://localhost:1313/> in your browser to view the example site.
|
||||
- When you make changes to the frontend code, build the complete frontend program using `pnpm build:all`.
|
||||
- The JavaScript and CSS code will be found in `ui/artalk/dist`.
|
||||
- The frontend code is automatically deployed to NPM by GitHub Actions.
|
||||
|
||||
Here is the default admin account (only created in test mode):
|
||||
5. **Building Backend Code**:
|
||||
|
||||
```yaml
|
||||
name: "admin"
|
||||
email: "admin@test.com"
|
||||
password: "admin"
|
||||
```
|
||||
- When you make changes to the backend code, note that the backend program embeds the frontend code.
|
||||
- Before building the backend, run `make build-frontend` (which runs `scripts/build-frontend.sh`). This will place the embedded frontend main program and sidebar frontend program in `/public`.
|
||||
- Then, build the backend program using `make build`.
|
||||
|
||||
More exploration:
|
||||
|
||||
- **Developing Artalk Plugins or Themes**:
|
||||
Refer to the [Artalk Plugin Development Guide](https://artalk.js.org/develop/plugin.html).
|
||||
|
||||
- **Makefile**:
|
||||
You can explore the process and commands in the `Makefile`.
|
||||
|
||||
- **Automated CI on GitHub**:
|
||||
The automated CI pipeline on GitHub, located in the `.github/workflows` directory, builds the frontend, backend, and Docker images. It publishes Docker images to Docker Hub, handles npm package publishing, and creates GitHub Releases. And also testing the frontend and backend.
|
||||
|
||||
The following sections provide more detailed information on the development workflow.
|
||||
|
||||
## 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.
|
||||
Artalk is a monorepo project, meaning all the source code resides in a single repository. Despite this, the frontend and backend are kept separate. The frontend part is located in the `./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.
|
||||
- `local/` - The local example site. 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.
|
||||
### Directory Overview
|
||||
|
||||
## Translation
|
||||
- `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.
|
||||
- `data/`: The local data. (This directory is ignored by git).
|
||||
- `docs/`: The documentation site source code.
|
||||
- `i18n/`: The translation files.
|
||||
- `local/`: The local files. (This directory is ignored by git).
|
||||
- `internal/`: The Go source code for the internal packages.
|
||||
- `public/`: The static files. Built frontend files will be copied here.
|
||||
- `scripts/`: The scripts for development and building.
|
||||
- `server/`: The Go source code for the backend.
|
||||
- `ui/`: The UI source code for the frontend.
|
||||
- `.github/`: The GitHub Actions workflows.
|
||||
- `.vscode/`: The VSCode settings.
|
||||
|
||||
Artalk aims to be a multilingual project. If you would like to contribute to the translation, here are some tips:
|
||||
## Backend Development
|
||||
|
||||
If you just write some new features or do some fixes/refactoring, use the following command to generate the translation template
|
||||
### Run Backend
|
||||
|
||||
1. **Create Configuration File**:
|
||||
|
||||
- Copy `./conf/artalk.example.yml` to the root directory.
|
||||
- Rename it to `artalk.yml`.
|
||||
- Modify the file as needed.
|
||||
|
||||
2. **Start Backend Program**:
|
||||
|
||||
- Run `make dev` to start the backend on port `23366`.
|
||||
- Access it via `http://localhost:23366`.
|
||||
- It's recommended to keep the default port for testing.
|
||||
- (Alternative) Use `ARGS="version" make dev` to pass startup parameters (default is `ARGS="server"`).
|
||||
|
||||
The `make dev` builds the backend with debugging symbols, which is convenient for debugging with GDB.
|
||||
|
||||
If you are using the VSCode, you can use the `F5` key (or RUN button) to start debugging the backend program directly.
|
||||
|
||||
### Build Backend
|
||||
|
||||
1. **Fetch Latest Git Tag**:
|
||||
Execute `git fetch --tags` to get the latest git tag information. This tag will be used as the version number of the backend app. Alternatively, specify it using environment variables: `VERSION="v1.0.0"` and `COMMIT_HASH="66128e"`.
|
||||
|
||||
2. **Build Frontend**:
|
||||
Run `make build-fronted` to build the frontend, as the backend app embeds the frontend code.
|
||||
|
||||
3. **Build Backend**:
|
||||
Execute `make build` to build the backend program.
|
||||
|
||||
The built binary file will be located in the `./bin` directory.
|
||||
|
||||
## Frontend Development (UI)
|
||||
|
||||
### Run Frontend
|
||||
|
||||
1. **Install Dependencies**:
|
||||
Run `pnpm install` to install all necessary dependencies.
|
||||
|
||||
2. **Start Development Server**:
|
||||
Execute `pnpm dev --port 5173` to start the frontend development server.
|
||||
|
||||
3. **Access the Frontend**:
|
||||
The frontend application will run on port `5173` by default. Access it in your browser at `http://localhost:5173`.
|
||||
|
||||
4. **Sidebar Development**:
|
||||
To run the sidebar part of the frontend, use `pnpm dev:sidebar`.
|
||||
|
||||
The ports `5173` and `23367` are used for frontend development and testing. These ports are included in the `ATK_TRUSTED_DOMAINS` configuration of the backend program.
|
||||
|
||||
### Build Frontend
|
||||
|
||||
To build the frontend, run the following command:
|
||||
|
||||
```sh
|
||||
go run ./internal/i18n/gen -w . -d .
|
||||
make build-frontend
|
||||
```
|
||||
|
||||
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.
|
||||
The compiled JavaScript and CSS files will be located in the `/public` directory, embedded into the backend program.
|
||||
|
||||
For more details, refer to the `scripts/build-frontend.sh` script.
|
||||
|
||||
## Docker Development
|
||||
|
||||
### Build Docker Image
|
||||
|
||||
To build the Docker image, run the following command:
|
||||
|
||||
```sh
|
||||
docker build -t artalk:TAG .
|
||||
```
|
||||
|
||||
Replace `TAG` with the desired tag name (e.g., `latest`).
|
||||
|
||||
### Skip Frontend Build
|
||||
|
||||
If you have already built the frontend outside the Docker container, you can skip the frontend build process inside the container to speed up the build. Use the following command:
|
||||
|
||||
```sh
|
||||
docker build --build-arg SKIP_FRONTEND_BUILD=true -t artalk:latest .
|
||||
```
|
||||
|
||||
For more details, refer to the `Dockerfile`.
|
||||
|
||||
## Testing
|
||||
|
||||
### Testing the Backend
|
||||
|
||||
To test the backend Go program, you can use the following commands:
|
||||
|
||||
- **Unit Testing**:
|
||||
Run `make test` to execute unit tests. The results will be outputted in the terminal.
|
||||
|
||||
- **Test Coverage**:
|
||||
Run `make test-coverage` to check the code test coverage.
|
||||
|
||||
- **HTML Coverage Report**:
|
||||
Run `make test-coverage-html` to generate and browse the test coverage report in HTML format.
|
||||
|
||||
- **Run Specific Tests**:
|
||||
To run specific tests, use the command with an environment variable:
|
||||
```sh
|
||||
TEST_PATHS="./server/..." make test
|
||||
```
|
||||
|
||||
### Testing the Frontend
|
||||
|
||||
Frontend testing includes unit tests and end-to-end (E2E) tests.
|
||||
|
||||
- **Unit Testing**:
|
||||
The unit tests for the frontend are conducted using Vitest. Run `pnpm test` to start the unit testing.
|
||||
|
||||
- **End-to-End Testing**:
|
||||
E2E testing is conducted using Playwright. To start the E2E testing, run:
|
||||
```sh
|
||||
make test-frontend-e2e
|
||||
```
|
||||
|
||||
### Continuous Integration for Testing
|
||||
|
||||
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.
|
||||
|
||||
## Configurations
|
||||
|
||||
The template configuration files are located in the `/conf` directory, with templates annotated in multiple languages named in the format `artalk.example.[lang].yml` (e.g., `artalk.example.zh-CN.yml`). Artalk will parse these template configuration files to generate the settings interface, environment variable names, and documentation. To improve performance, some data will be cached during the program's runtime to eliminate parsing time.
|
||||
|
||||
In the `internal/config/config.go` file, there is a definition of the `Config` struct, which is used to parse the yml files into Go structs. This struct contains more precise type definitions. If you need to add, modify, or delete a configuration item, you must make changes to both the configuration file templates in `/conf` and the `Config` struct.
|
||||
|
||||
When you modify the configuration files, please run the following command to update the configuration data cache:
|
||||
|
||||
```sh
|
||||
make update-conf
|
||||
```
|
||||
|
||||
To update the environment variable documentation, you can run the command:
|
||||
|
||||
```sh
|
||||
make update-conf-docs
|
||||
```
|
||||
|
||||
## Documentations
|
||||
|
||||
The documentation consists of three parts, each associated with a specific package: **Guide documentation**, **Landing page**, and **Swagger API documentation**. Below is an organized summary of the relevant commands and their outputs for each package.
|
||||
|
||||
### Guide Documentation (`docs`)
|
||||
|
||||
- **Directory**: `docs/docs/guide`
|
||||
- **Dev Command**: `pnpm -F docs dev:docs`
|
||||
- **Build Command**: `pnpm -F docs build:docs`
|
||||
- **Output Directory**: `docs/docs/.vitepress/dist`
|
||||
|
||||
### Landing Page (`docs-landing`)
|
||||
|
||||
- **Directory**: `docs/landing`
|
||||
- **Dev Command**: `pnpm -F docs-landing dev:landing`
|
||||
- **Build Command**: `pnpm -F docs-landing build:landing`
|
||||
- **Output Directory**: `docs/landing/dist`
|
||||
|
||||
### Swagger API Documentation (`docs-swagger`)
|
||||
|
||||
- **Directory**: `docs/swagger`
|
||||
- **Build Command**: `make update-swagger`
|
||||
- **Output Directory**: `docs/swagger`
|
||||
|
||||
Swagger definitions are located in the backend code at `/server`. After modifying the Swagger definitions, running the build command will update the Swagger docs and generate HTTP client code at `/ui/artalk/src/api/v2.ts`.
|
||||
|
||||
### Combined Build
|
||||
|
||||
- **Command to Build All Packages**: `pnpm build:docs`
|
||||
- **Combined Output Directory**: `docs/docs/.vitepress/dist`
|
||||
|
||||
This command builds and combines all three packages into a single directory, ready for publishing.
|
||||
|
||||
### Environment Variable Documentation
|
||||
|
||||
- **File**: `docs/docs/guide/env.md`
|
||||
- **Command to Update**: `make update-conf-docs`
|
||||
|
||||
This command reads the configuration template file located in `/conf` to update the environment variable documentation. Run this command after modifying the configuration template file.
|
||||
|
||||
## Translation (i18n)
|
||||
|
||||
If you write new features or make fixes/refactoring, use the following command to incrementally generate the translation files by parsing the source code for `i18n.T` function calls:
|
||||
|
||||
```sh
|
||||
make update-i18n
|
||||
```
|
||||
|
||||
If you're not a programmer and would like to help improve the translation, you can edit the translation files directly in the `/i18n` directory and then submit a pull request.
|
||||
|
||||
## The End
|
||||
|
||||
Thank you for your patience in reading this and for your interest and support in Artalk. We understand that the success of an open-source project relies on the contributions of every developer. Whether through code, documentation, testing, or feedback, your participation is the driving force behind the project's continuous progress. We sincerely invite you to join us in improving and enhancing Artalk.
|
||||
|
||||
By collaborating, we can create a more robust and versatile platform that benefits the entire community. Your unique insights and expertise are invaluable, and together, we can achieve remarkable advancements. Whether you are a seasoned developer or just starting, there is always a place for your contributions in our project.
|
||||
|
||||
If you have any questions or need assistance, please feel free to ask in the issue section on our GitHub page: https://github.com/ArtalkJS/Artalk/issues. Thank you once again for your support, and we look forward to working with you.
|
||||
+25
-8
@@ -1,12 +1,11 @@
|
||||
### build Artalk
|
||||
FROM golang:1.20.1-alpine3.17 as builder
|
||||
FROM golang:1.22.7-alpine3.20 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@7.25.0
|
||||
&& apk add --no-cache make git gcc musl-dev bash
|
||||
|
||||
# download go deps
|
||||
# (cache by separating the downloading of deps)
|
||||
@@ -16,20 +15,38 @@ RUN go mod download
|
||||
# copy source code
|
||||
COPY . .
|
||||
|
||||
# build
|
||||
## build UI
|
||||
ARG SKIP_UI_BUILD=false
|
||||
|
||||
# install ui build toolchain
|
||||
RUN set -ex \
|
||||
&& export VERSION=$(git describe --tags --abbrev=0) \
|
||||
&& export COMMIT_SHA=$(git rev-parse --short HEAD) \
|
||||
&& if [ "$SKIP_UI_BUILD" = "false" ]; then \
|
||||
apk add --no-cache nodejs npm \
|
||||
&& npm install -g pnpm@9.10.0 \
|
||||
;fi
|
||||
|
||||
RUN set -ex \
|
||||
&& if [ "$SKIP_UI_BUILD" = "false" ]; then \
|
||||
make build-frontend \
|
||||
;fi
|
||||
|
||||
## build App
|
||||
ARG APP_VERSION=""
|
||||
ARG APP_COMMIT_HASH=""
|
||||
|
||||
RUN set -ex \
|
||||
&& if [[ ! -z "$APP_VERSION" ]]; then export VERSION=$APP_VERSION ;fi \
|
||||
&& if [[ ! -z "$APP_COMMIT_HASH" ]]; then export COMMIT_HASH=$APP_COMMIT_HASH ;fi \
|
||||
&& make build
|
||||
|
||||
### build final image
|
||||
FROM alpine:3.17
|
||||
FROM alpine:3.20
|
||||
|
||||
# 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}
|
||||
ENV TZ=${TZ}
|
||||
|
||||
COPY --from=builder /source/bin/artalk /artalk
|
||||
|
||||
|
||||
@@ -1,165 +1,21 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
The MIT License (MIT)
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser 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
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
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.
|
||||
@@ -1,54 +1,71 @@
|
||||
PKG_NAME := github.com/ArtalkJS/Artalk
|
||||
PKG_NAME := github.com/artalkjs/artalk/v2
|
||||
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
|
||||
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
all: install build
|
||||
|
||||
install:
|
||||
go mod tidy
|
||||
|
||||
build: build-frontend
|
||||
run: all
|
||||
$(BIN_NAME) $(ARGS)
|
||||
|
||||
build:
|
||||
go build \
|
||||
-ldflags "-s -w -X $(PKG_NAME)/internal/config.Version=$(VERSION) \
|
||||
-X $(PKG_NAME)/internal/config.CommitHash=$(COMMIT_HASH)" \
|
||||
-ldflags "-s -w" \
|
||||
-o $(BIN_NAME) \
|
||||
$(PKG_NAME)
|
||||
|
||||
build-frontend:
|
||||
./scripts/build-frontend.sh
|
||||
|
||||
run: all
|
||||
$(BIN_NAME) $(ARGS)
|
||||
|
||||
build-debug:
|
||||
@echo "Building Artalk $(VERSION) for debugging..."
|
||||
@echo "Building Artalk 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
|
||||
ATK_SITE_DEFAULT="ArtalkDocs" \
|
||||
ATK_TRUSTED_DOMAINS="http://localhost:5173 http://localhost:23367" \
|
||||
$(BIN_NAME) $(ARGS)
|
||||
|
||||
test:
|
||||
$(GOTEST) -timeout 20m ./internal/...
|
||||
$(GOTEST) -timeout 20m $(or $(TEST_PATHS), ./...)
|
||||
|
||||
test-coverage:
|
||||
$(GOTEST) -cover ./...
|
||||
$(GOTEST) -cover $(or $(TEST_PATHS), ./...)
|
||||
|
||||
test-coverage-html:
|
||||
$(GOTEST) -v -coverprofile=coverage.out $(or $(TEST_PATHS), ./...)
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
test-frontend-e2e:
|
||||
./scripts/frontend-e2e-test.sh $(if $(REPORT), --show-report)
|
||||
|
||||
update-i18n:
|
||||
go generate ./internal/i18n
|
||||
|
||||
update-conf:
|
||||
go generate ./internal/config
|
||||
|
||||
update-conf-docs:
|
||||
go run ./internal/config/meta/gen --format markdown --locale en -o ./docs/docs/en/guide/env.md
|
||||
go run ./internal/config/meta/gen --format markdown --locale zh-CN -o ./docs/docs/zh/guide/env.md
|
||||
|
||||
update-docs-features:
|
||||
pnpm -F docs-landing update:readme
|
||||
|
||||
update-swagger:
|
||||
swag init -g server/server.go --output ./docs/swagger
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
swag init -g server/server.go --output ./docs/swagger --requiredByDefault
|
||||
pnpm -r swagger:build-http-client
|
||||
|
||||
docker-build:
|
||||
./scripts/docker-build.sh
|
||||
@@ -56,6 +73,8 @@ docker-build:
|
||||
docker-push:
|
||||
./scripts/docker-build.sh --push
|
||||
|
||||
.PHONY: all install build build-frontend build-debug \
|
||||
dev test test-coverage update-i18n \
|
||||
.PHONY: all install run build build-frontend build-debug dev \
|
||||
test test-coverage test-coverage-html test-frontend-e2e \
|
||||
update-i18n update-conf update-conf-docs \
|
||||
update-docs-features update-swagger \
|
||||
docker-build docker-push;
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
<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
|
||||
|
||||
[LGPL-3.0](./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)
|
||||
@@ -4,107 +4,110 @@
|
||||
|
||||
# Artalk
|
||||
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://bundlephobia.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://www.npmjs.com/package/artalk)
|
||||
[](https://hub.docker.com/r/artalk/artalk-go)
|
||||
[](https://circleci.com/gh/ArtalkJS/Artalk/tree/master)
|
||||
[](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)
|
||||
|
||||
> 🌌 Golang 自托管评论系统
|
||||
[Homepage](https://artalk.js.org) • [Documentation](https://artalk.js.org/en/guide/deploy.html) • [Latest Release](https://github.com/ArtalkJS/Artalk/releases) • [Changelog](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md) • [简体中文](./README.zh.md)
|
||||
|
||||
[English](./README.en.md) / [官方文档](https://artalk.js.org) / [最新版本](https://github.com/ArtalkJS/Artalk/releases)
|
||||
Artalk is an intuitive yet feature-rich comment system, ready for immediate deployment into any blog, website, or web application.
|
||||
|
||||
---
|
||||
- 🍃 Client ~40KB, crafted with pure Vanilla JS, framework-agnostic
|
||||
- 🍱 Server powered by Golang, offering efficient and lightweight cross-platform performance
|
||||
- 🐳 One-click deployment via Docker, ensuring ease and speed
|
||||
- 🌈 Open-source software, self-hosted with privacy as a priority
|
||||
|
||||
- 🍃 轻量 (~30kB)
|
||||
- 👨👧👦 安全 (自托管)
|
||||
- 🐳 易上手 (防脱发)
|
||||
- 🍱 Golang 后端 (快速 / 跨平台)
|
||||
- 🌊 Vanilla × TypeScript × Vite (纯天然 / 无依赖)
|
||||
## Features
|
||||
|
||||
## 特性
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
- 侧 边 栏 :支持多站点集中化管理
|
||||
- 通知中心:红点的标记 / 提及列表
|
||||
- 身份验证:徽标自定义 / 密码验证
|
||||
- 评论审核:反垃圾检测 / 频率限制
|
||||
- 表情符号:插入表情包 / 快速导入
|
||||
- 邮件提醒:模版自定义 / 多管理员
|
||||
- 站点隔离:管理员分配 / 多个站点
|
||||
- 页面管理:标题可显示 / 快速跳转
|
||||
- 图片上传:上传到本地 / 多种图床
|
||||
- 多元推送:支持 钉钉 飞书 TG
|
||||
- 无限层级:可切换为平铺模式
|
||||
- 评论投票:赞同还是反对评论
|
||||
- 评论排序:按热度或时间排序
|
||||
- 评论置顶:重要消息置顶显示
|
||||
- 只看作者:仅显示作者的评论
|
||||
- 说说模式:仅自己可发布评论
|
||||
- 异步处理:发送评论无需等待
|
||||
- 滚动加载:评论内容分页处置
|
||||
- 自动保存:用户输入防丢功能
|
||||
- 自动填充:用户链接自动填充
|
||||
- 实时预览:评论内容实时预览
|
||||
- 夜间模式:防止眼部疾病伤害
|
||||
- 评论折叠:这个不打算给你康
|
||||
- 数据备份:防止评论数据丢失
|
||||
- 数据迁移:快速切换评论系统
|
||||
- Markdown:默认支持 MD 语法
|
||||
- 支持 Latex:提供集成 Katex 插件
|
||||
- 使用 [Vite](https://github.com/vitejs/vite):属于开发者的极致体验
|
||||
<!-- features -->
|
||||
* [Sidebar](https://artalk.js.org/guide/frontend/sidebar.html): Quick management, intuitive browsing
|
||||
* [Social Login](https://artalk.js.org/guide/frontend/auth.html): Fast login via social accounts
|
||||
* [Email Notification](https://artalk.js.org/guide/backend/email.html): Various sending methods, email templates
|
||||
* [Diverse Push](https://artalk.js.org/guide/backend/admin_notify.html): Multiple push methods, notification templates
|
||||
* [Site Notification](https://artalk.js.org/guide/frontend/sidebar.html): Red dot marks, mention list
|
||||
* [Captcha](https://artalk.js.org/guide/backend/captcha.html): Various verification types, frequency limits
|
||||
* [Comment Moderation](https://artalk.js.org/guide/backend/moderator.html): Content detection, spam interception
|
||||
* [Image Upload](https://artalk.js.org/guide/backend/img-upload.html): Custom upload, supports image hosting
|
||||
* [Markdown](https://artalk.js.org/guide/intro.html): Supports Markdown syntax
|
||||
* [Emoji Pack](https://artalk.js.org/guide/frontend/emoticons.html): Compatible with OwO, quick integration
|
||||
* [Multi-Site](https://artalk.js.org/guide/backend/multi-site.html): Site isolation, centralized management
|
||||
* [Admin](https://artalk.js.org/guide/backend/multi-site.html): Password verification, badge identification
|
||||
* [Page Management](https://artalk.js.org/guide/frontend/sidebar.html): Quick view, one-click title navigation
|
||||
* [Page View Statistics](https://artalk.js.org/guide/frontend/pv.html): Easily track page views
|
||||
* [Hierarchical Structure](https://artalk.js.org/guide/frontend/config.html#nestmax): Nested paginated list, infinite scroll
|
||||
* [Comment Voting](https://artalk.js.org/guide/frontend/config.html#vote): Upvote or downvote comments
|
||||
* [Comment Sorting](https://artalk.js.org/guide/frontend/config.html#listsort): Various sorting options, freely selectable
|
||||
* [Comment Search](https://artalk.js.org/guide/frontend/sidebar.html): Quick comment content search
|
||||
* [Comment Pinning](https://artalk.js.org/guide/frontend/sidebar.html): Pin important messages
|
||||
* [View Author Only](https://artalk.js.org/guide/frontend/config.html): Show only the author's comments
|
||||
* [Comment Jump](https://artalk.js.org/guide/intro.html): Quickly jump to quoted comment
|
||||
* [Auto Save](https://artalk.js.org/guide/frontend/config.html): Content loss prevention
|
||||
* [IP Region](https://artalk.js.org/guide/frontend/ip-region.html): Display user's IP region
|
||||
* [Data Migration](https://artalk.js.org/guide/transfer.html): Free migration, quick backup
|
||||
* [Image Lightbox](https://artalk.js.org/guide/frontend/lightbox.html): Quick integration of image lightbox
|
||||
* [Image Lazy Load](https://artalk.js.org/guide/frontend/img-lazy-load.html): Lazy load images, optimize experience
|
||||
* [Latex](https://artalk.js.org/guide/frontend/latex.html): Integrate Latex formula parsing
|
||||
* [Night Mode](https://artalk.js.org/guide/frontend/config.html#darkmode): Switch to night mode
|
||||
* [Extension Plugin](https://artalk.js.org/develop/plugin.html): Create more possibilities
|
||||
* [Multi-Language](https://artalk.js.org/guide/frontend/i18n.html): Switch between multiple languages
|
||||
* [Command Line](https://artalk.js.org/guide/backend/config.html): Command line operation management
|
||||
* [API Documentation](https://artalk.js.org/http-api.html): Provides OpenAPI format documentation
|
||||
* [Program Upgrade](https://artalk.js.org/guide/backend/update.html): Version check, one-click upgrade
|
||||
<!-- /features -->
|
||||
|
||||
更多内容请查看:[“**介绍文档**”](https://artalk.js.org/guide/intro.html)
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## 食用方针
|
||||
## Installation
|
||||
|
||||
前往:[“**部署文档**”](https://artalk.js.org/guide/deploy.html)
|
||||
|
||||
```ts
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
site: 'Artalk 的博客',
|
||||
server: 'https://artalk.example.com'
|
||||
})
|
||||
```
|
||||
|
||||
### Docker
|
||||
Deploy Artalk Server with Docker in one step:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
artalk/artalk-go
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
-e "TZ=America/New_York" \
|
||||
-e "ATK_LOCALE=en" \
|
||||
-e "ATK_SITE_DEFAULT=Artalk Blog" \
|
||||
-e "ATK_SITE_URL=https://example.com" \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
Integrate Artalk Client into your webpage:
|
||||
|
||||
```bash
|
||||
vim docker-compose.yaml
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
```ts
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
site: 'Artalk Blog',
|
||||
server: 'https://artalk.example.com',
|
||||
pageKey: '/2018/10/02/hello-world.html'
|
||||
})
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3.5"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
ports:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
```
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
We offer various installation methods, including binary files, go install, and package managers for Linux distributions.
|
||||
|
||||
## Development
|
||||
[**Learn More →**](https://artalk.js.org/en/guide/deploy.html)
|
||||
|
||||
see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
## For Developers
|
||||
|
||||
Pull requests are welcome!
|
||||
|
||||
See [Development](https://artalk.js.org/en/develop/) and [Contributing](./CONTRIBUTING.md) for information on working with the codebase, getting a local development setup, and contributing changes.
|
||||
|
||||
## Contributors
|
||||
|
||||
Your contributions enrich the open-source community, fostering learning, inspiration, and innovation. We deeply value your involvement. Thank you for being a vital part of our community! 🥰
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/graphs/contributors)
|
||||
|
||||
## Supporters
|
||||
@@ -113,111 +116,17 @@ see [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
## 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] [极验](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
|
||||
|
||||
<a href="https://trendshift.io/repositories/6290" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6290" alt="ArtalkJS%2FArtalk | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||
[](https://starchart.cc/ArtalkJS/Artalk)
|
||||
|
||||
## License
|
||||
|
||||
[LGPL-3.0](./LICENSE)
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FArtalkJS%2FArtalk?ref=badge_shield)
|
||||
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<p align="center">
|
||||
<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://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)
|
||||
|
||||
[官方网站](https://artalk.js.org) • [最新版本](https://github.com/ArtalkJS/Artalk/releases) • [更新日志](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md) • [English](./README.md)
|
||||
|
||||
Artalk 是一款简单易用但功能丰富的评论系统,你可以开箱即用地部署并置入任何博客、网站、Web 应用。
|
||||
|
||||
- 🍃 前端 ~40KB,纯天然 Vanilla JS
|
||||
- 🍱 后端 Golang,高效轻量跨平台
|
||||
- 🐳 通过 Docker 一键部署,方便快捷
|
||||
- 🌈 开源程序,自托管,隐私至上
|
||||
|
||||
## 特性
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
<!-- features -->
|
||||
* [侧边栏](https://artalk.js.org/guide/frontend/sidebar.html): 快速管理、直观浏览
|
||||
* [社交登录](https://artalk.js.org/guide/frontend/auth.html): 通过社交账号快速登录
|
||||
* [邮件通知](https://artalk.js.org/guide/backend/email.html): 多种发送方式、邮件模板
|
||||
* [多元推送](https://artalk.js.org/guide/backend/admin_notify.html): 多种推送方式、通知模版
|
||||
* [站内通知](https://artalk.js.org/guide/frontend/sidebar.html): 红点标记、提及列表
|
||||
* [验证码](https://artalk.js.org/guide/backend/captcha.html): 多种验证类型、频率限制
|
||||
* [评论审核](https://artalk.js.org/guide/backend/moderator.html): 内容检测、垃圾拦截
|
||||
* [图片上传](https://artalk.js.org/guide/backend/img-upload.html): 自定义上传、支持图床
|
||||
* [Markdown](https://artalk.js.org/guide/intro.html): 支持 Markdown 语法
|
||||
* [表情包](https://artalk.js.org/guide/frontend/emoticons.html): 兼容 OwO,快速集成
|
||||
* [多站点](https://artalk.js.org/guide/backend/multi-site.html): 站点隔离、集中管理
|
||||
* [管理员](https://artalk.js.org/guide/backend/multi-site.html): 密码验证、徽章标识
|
||||
* [页面管理](https://artalk.js.org/guide/frontend/sidebar.html): 快速查看、标题一键跳转
|
||||
* [浏览量统计](https://artalk.js.org/guide/frontend/pv.html): 轻松统计网页浏览量
|
||||
* [层级结构](https://artalk.js.org/guide/frontend/config.html#nestmax): 嵌套分页列表、滚动加载
|
||||
* [评论投票](https://artalk.js.org/guide/frontend/config.html#vote): 赞同或反对评论
|
||||
* [评论排序](https://artalk.js.org/guide/frontend/config.html#listsort): 多种排序方式,自由选择
|
||||
* [评论搜索](https://artalk.js.org/guide/frontend/sidebar.html): 快速搜索评论内容
|
||||
* [评论置顶](https://artalk.js.org/guide/frontend/sidebar.html): 重要消息置顶显示
|
||||
* [仅看作者](https://artalk.js.org/guide/frontend/config.html): 仅显示作者的评论
|
||||
* [评论跳转](https://artalk.js.org/guide/intro.html): 快速跳转到引用的评论
|
||||
* [自动保存](https://artalk.js.org/guide/frontend/config.html): 输入内容防丢功能
|
||||
* [IP 属地](https://artalk.js.org/guide/frontend/ip-region.html): 用户 IP 属地展示
|
||||
* [数据迁移](https://artalk.js.org/guide/transfer.html): 自由迁移、快速备份
|
||||
* [图片灯箱](https://artalk.js.org/guide/frontend/lightbox.html): 图片灯箱快速集成
|
||||
* [图片懒加载](https://artalk.js.org/guide/frontend/img-lazy-load.html): 延迟加载图片,优化体验
|
||||
* [Latex](https://artalk.js.org/guide/frontend/latex.html): Latex 公式解析集成
|
||||
* [夜间模式](https://artalk.js.org/guide/frontend/config.html#darkmode): 夜间模式切换
|
||||
* [扩展插件](https://artalk.js.org/develop/plugin.html): 创造更多可能性
|
||||
* [多语言](https://artalk.js.org/guide/frontend/i18n.html): 多国语言切换
|
||||
* [命令行](https://artalk.js.org/guide/backend/config.html): 命令行操作管理能力
|
||||
* [API 文档](https://artalk.js.org/http-api.html): 提供 OpenAPI 格式文档
|
||||
* [程序升级](https://artalk.js.org/guide/backend/update.html): 版本检测,一键升级
|
||||
<!-- /features -->
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## 安装
|
||||
|
||||
通过 Docker 一键部署 Artalk 服务器:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name artalk \
|
||||
-p 8080:23366 \
|
||||
-v $(pwd)/data:/data \
|
||||
-e "TZ=Asia/Shanghai" \
|
||||
-e "ATK_LOCALE=zh-CN" \
|
||||
-e "ATK_SITE_DEFAULT=Artalk 的博客" \
|
||||
-e "ATK_SITE_URL=https://example.com" \
|
||||
artalk/artalk-go
|
||||
```
|
||||
|
||||
在你的网页中引入 Artalk 客户端:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
```ts
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
site: 'Artalk 的博客',
|
||||
server: 'https://artalk.example.com',
|
||||
pageKey: '/2018/10/02/hello-world.html'
|
||||
})
|
||||
```
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
我们提供多种安装方法,包括二进制文件、`go install` 和通过 Linux 发行版的包管理器安装。
|
||||
|
||||
[**了解更多 →**](https://artalk.js.org/zh/guide/deploy.html)
|
||||
|
||||
## 参与开发
|
||||
|
||||
我们欢迎你的 Pull Request!
|
||||
|
||||
有关如何使用代码库、设置本地开发环境和贡献更改的信息,请参阅 [开发文档](https://artalk.js.org/zh/develop/) 和 [贡献指南](./CONTRIBUTING.md)。
|
||||
|
||||
## 贡献者们
|
||||
|
||||
你的贡献丰富了开源社区,促进了学习、灵感和创新。我们非常重视你的参与。感谢你成为我们社区的重要一员!🥰
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/graphs/contributors)
|
||||
|
||||
## 支持者们
|
||||
|
||||
[](https://github.com/ArtalkJS/Artalk/stargazers)
|
||||
|
||||
## Repobeats 分析
|
||||
|
||||

|
||||
|
||||
## Star 趋势
|
||||
|
||||
<a href="https://trendshift.io/repositories/6290" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6290" alt="ArtalkJS%2FArtalk | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||
[](https://starchart.cc/ArtalkJS/Artalk)
|
||||
|
||||
## 开源许可协议
|
||||
|
||||
[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)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Reporting Security Issues
|
||||
|
||||
The features of Artalk are continuously being improved, and we are also striving to enhance security. However, potential issues may still exist. If you discover any security vulnerabilities in Artalk, please promptly contact us via [GitHub Issues](https://github.com/ArtalkJS/Artalk/issues) or email at artalkjs@gmail.com. We will make every effort to address the issue as quickly as possible. Thank you for your support!
|
||||
+59
-51
@@ -2,72 +2,80 @@ package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/internal/entity"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/query"
|
||||
"github.com/ArtalkJS/Artalk/internal/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/artalkjs/artalk/v2/internal/entity"
|
||||
"github.com/artalkjs/artalk/v2/internal/i18n"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/artalkjs/artalk/v2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var adminCmd = &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Create or edit an administrator account",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
core.LoadCore(cfgFile, workDir)
|
||||
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("--------------------------------")
|
||||
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println(" " + i18n.T("Create admin account"))
|
||||
fmt.Println("--------------------------------")
|
||||
// get from flags
|
||||
username, _ := cmd.Flags().GetString("name")
|
||||
email, _ := cmd.Flags().GetString("email")
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
|
||||
username, email, password, err := credentials()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
findUser := query.FindUser(username, email)
|
||||
if !findUser.IsEmpty() {
|
||||
findUser.SetPasswordEncrypt(password)
|
||||
if err := query.UpdateUser(&findUser); err != nil {
|
||||
logrus.Fatal(err)
|
||||
if username == "" || email == "" || password == "" {
|
||||
var err error
|
||||
username, email, password, err = credentials()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info(i18n.T("{{name}} already exists", map[string]interface{}{"name": i18n.T("Account")}) +
|
||||
", " + i18n.T("Password updated"))
|
||||
return
|
||||
}
|
||||
findUser := app.Dao().FindUser(username, email)
|
||||
if !findUser.IsEmpty() {
|
||||
findUser.SetPasswordEncrypt(password)
|
||||
findUser.IsAdmin = true
|
||||
if err := app.Dao().UpdateUser(&findUser); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
user := entity.User{
|
||||
Name: username,
|
||||
Email: email,
|
||||
IsAdmin: true,
|
||||
BadgeName: i18n.T("Admin"),
|
||||
BadgeColor: "#0083FF",
|
||||
}
|
||||
user.SetPasswordEncrypt(password)
|
||||
log.Info(i18n.T("{{name}} already exists", map[string]interface{}{"name": i18n.T("Account")}) +
|
||||
", " + i18n.T("Password updated"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := query.CreateUser(&user); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
user := entity.User{
|
||||
Name: username,
|
||||
Email: email,
|
||||
IsAdmin: true,
|
||||
BadgeName: i18n.T("Admin"),
|
||||
BadgeColor: "#0083FF",
|
||||
}
|
||||
user.SetPasswordEncrypt(password)
|
||||
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println(" Name: " + username)
|
||||
fmt.Println(" Mail: " + email)
|
||||
fmt.Println("--------------------------------")
|
||||
},
|
||||
}
|
||||
if err := app.Dao().CreateUser(&user); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(adminCmd)
|
||||
fmt.Println("--------------------------------")
|
||||
fmt.Println(" Name: " + username)
|
||||
fmt.Println(" Mail: " + email)
|
||||
fmt.Println("--------------------------------")
|
||||
},
|
||||
}
|
||||
|
||||
flag(adminCmd, "name", "", i18n.T("Username"))
|
||||
flag(adminCmd, "email", "", i18n.T("Email"))
|
||||
flag(adminCmd, "password", "", i18n.T("Password"))
|
||||
|
||||
return adminCmd
|
||||
}
|
||||
|
||||
func credentials() (string, string, string, error) {
|
||||
@@ -85,7 +93,7 @@ func credentials() (string, string, string, error) {
|
||||
return "", "", "", err
|
||||
}
|
||||
if !utils.ValidateEmail(strings.TrimSpace(email)) {
|
||||
return "", "", "", errors.New("invalid email format")
|
||||
return "", "", "", fmt.Errorf("invalid email format")
|
||||
}
|
||||
|
||||
fmt.Print(i18n.T("Enter {{name}}", map[string]interface{}{"name": i18n.T("Password")}) + ": ")
|
||||
@@ -107,7 +115,7 @@ func credentials() (string, string, string, error) {
|
||||
rePassword := strings.TrimSpace(string(byteRePassword))
|
||||
|
||||
if rePassword != password {
|
||||
return "", "", "", errors.New("inconsistent password input")
|
||||
return "", "", "", fmt.Errorf("inconsistent password input")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(username), strings.TrimSpace(email), password, nil
|
||||
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/artalkjs/artalk/v2/internal/config"
|
||||
"github.com/artalkjs/artalk/v2/internal/core"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Banner = `
|
||||
________ ________ _________ ________ ___ ___ __
|
||||
|\ __ \|\ __ \|\___ ___\\ __ \|\ \ |\ \|\ \
|
||||
\ \ \|\ \ \ \|\ \|___ \ \_\ \ \|\ \ \ \ \ \ \/ /|_
|
||||
\ \ __ \ \ _ _\ \ \ \ \ \ __ \ \ \ \ \ ___ \
|
||||
\ \ \ \ \ \ \\ \| \ \ \ \ \ \ \ \ \ \____\ \ \\ \ \
|
||||
\ \__\ \__\ \__\\ _\ \ \__\ \ \__\ \__\ \_______\ \__\\ \__\
|
||||
\|__|\|__|\|__|\|__| \|__| \|__|\|__|\|_______|\|__| \|__|
|
||||
|
||||
Artalk (` + config.VersionString() + `)
|
||||
|
||||
-> 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: config.VersionString(),
|
||||
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/v2/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
|
||||
}
|
||||
+50
-53
@@ -7,69 +7,66 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/artransfer"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/ArtalkJS/Artalk/internal/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/artalkjs/artalk/v2/internal/artransfer"
|
||||
"github.com/artalkjs/artalk/v2/internal/i18n"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/artalkjs/artalk/v2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var 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) {
|
||||
core.LoadCore(cfgFile, workDir)
|
||||
|
||||
jsonStr, err := artransfer.ExportArtransString()
|
||||
if err != nil {
|
||||
logrus.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)
|
||||
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 {
|
||||
logrus.Fatal(err)
|
||||
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")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// mkdir -p
|
||||
if err := utils.EnsureDir(filepath.Dir(filename)); err != nil {
|
||||
logrus.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")
|
||||
}
|
||||
}
|
||||
|
||||
// touch
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
// mkdir -p
|
||||
if err := utils.EnsureDir(filepath.Dir(filename)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// >
|
||||
_, err2 := f.WriteString(jsonStr)
|
||||
if err2 != nil {
|
||||
logrus.Fatal(err2)
|
||||
}
|
||||
// touch
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
logrus.Info(i18n.T("Export complete") + ": " + filename)
|
||||
}
|
||||
},
|
||||
}
|
||||
// >
|
||||
_, err2 := f.WriteString(jsonStr)
|
||||
if err2 != nil {
|
||||
log.Fatal(err2)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
log.Info(i18n.T("Export complete") + ": " + filename)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return exportCmd
|
||||
}
|
||||
+23
-30
@@ -1,41 +1,34 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/artalkjs/artalk/v2/internal/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var 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) {
|
||||
// change working directory
|
||||
if workDir != "" {
|
||||
if err := os.Chdir(workDir); err != nil {
|
||||
logrus.Fatal("change working directory error ", err)
|
||||
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")
|
||||
|
||||
var (
|
||||
specificPath string
|
||||
isForce bool
|
||||
)
|
||||
if len(args) > 1 {
|
||||
specificPath = args[1]
|
||||
}
|
||||
isForce, _ = cmd.Flags().GetBool("force")
|
||||
|
||||
core.Gen(args[0], specificPath, isForce)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(genCmd)
|
||||
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
|
||||
}
|
||||
+43
-25
@@ -1,42 +1,60 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/artransfer"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/artalkjs/artalk/v2/internal/artransfer"
|
||||
"github.com/artalkjs/artalk/v2/internal/i18n"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var 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) {
|
||||
core.LoadCore(cfgFile, workDir) // load core
|
||||
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",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Prepare params
|
||||
params := &artransfer.ImportParams{}
|
||||
|
||||
parcelFile := args[0]
|
||||
if _, err := os.Stat(parcelFile); errors.Is(err, os.ErrNotExist) {
|
||||
logrus.Fatal(i18n.T("{{name}} not found", map[string]interface{}{"name": i18n.T("File")}))
|
||||
}
|
||||
// Parse JSON parameters from flags
|
||||
if jsonParams, _ := cmd.Flags().GetString("parameters"); jsonParams != "" {
|
||||
if err := json.Unmarshal([]byte(jsonParams), params); err != nil {
|
||||
log.Fatal("Failed to parse JSON parameters: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
payload := args[1:]
|
||||
payload = append(payload, "json_file:"+parcelFile)
|
||||
// If JSON file or JSON data is not provided in flags, try to get it from arguments
|
||||
if params.JsonFile == "" && params.JsonData == "" {
|
||||
if len(args) == 0 {
|
||||
log.Fatal(i18n.T("{{name}} is required", map[string]interface{}{"name": "FILENAME"}))
|
||||
}
|
||||
params.JsonFile = args[0]
|
||||
}
|
||||
|
||||
// import Artrans
|
||||
artransfer.RunImportArtrans(payload)
|
||||
// Parse flags to params
|
||||
if flagAssumeyes, err := cmd.Flags().GetBool("assumeyes"); err == nil {
|
||||
params.Assumeyes = flagAssumeyes
|
||||
}
|
||||
|
||||
logrus.Info(i18n.T("Import complete"))
|
||||
},
|
||||
}
|
||||
// Check if file exists if JsonFile is provided
|
||||
if params.JsonFile != "" {
|
||||
if _, err := os.Stat(params.JsonFile); errors.Is(err, os.ErrNotExist) {
|
||||
log.Fatal(i18n.T("{{name}} not found", map[string]interface{}{"name": i18n.T("File")}))
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(importCmd)
|
||||
// Run import
|
||||
artransfer.RunImportArtrans(app.Dao(), params)
|
||||
},
|
||||
}
|
||||
|
||||
flagPV(importCmd, "assumeyes", "y", false, "Automatically answer yes for all questions.")
|
||||
flagPV(importCmd, "parameters", "p", "", "JSON format parameters for the import command.")
|
||||
|
||||
return importCmd
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/config"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Version = config.Version + `/` + config.CommitHash
|
||||
|
||||
var Banner = `
|
||||
________ ________ _________ ________ ___ ___ __
|
||||
|\ __ \|\ __ \|\___ ___\\ __ \|\ \ |\ \|\ \
|
||||
\ \ \|\ \ \ \|\ \|___ \ \_\ \ \|\ \ \ \ \ \ \/ /|_
|
||||
\ \ __ \ \ _ _\ \ \ \ \ \ __ \ \ \ \ \ ___ \
|
||||
\ \ \ \ \ \ \\ \| \ \ \ \ \ \ \ \ \ \____\ \ \\ \ \
|
||||
\ \__\ \__\ \__\\ _\ \ \__\ \ \__\ \__\ \_______\ \__\\ \__\
|
||||
\|__|\|__|\|__|\|__| \|__| \|__|\|__|\|_______|\|__| \|__|
|
||||
|
||||
Artalk (` + Version + `)
|
||||
|
||||
-> A Selfhosted Comment System.
|
||||
-> https://artalk.js.org
|
||||
`
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
workDir string
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "artalk",
|
||||
Short: "Artalk: A Fast, Slight & Delightful 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.")
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
cobra.CheckErr(rootCmd.Execute())
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.SetVersionTemplate("Artalk ({{printf \"%s\" .Version}})\n")
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file path (defaults are './artalk.yml')")
|
||||
rootCmd.PersistentFlags().StringVarP(&workDir, "workdir", "w", "", "program working directory (defaults are './')")
|
||||
|
||||
// Version Command
|
||||
versionCmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Output Version Information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Artalk (" + Version + ")")
|
||||
},
|
||||
}
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
|
||||
// Config Command
|
||||
configCmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Output Config Information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
core.LoadConfOnly(cfgFile, workDir)
|
||||
buf, _ := json.MarshalIndent(config.Instance, "", " ")
|
||||
fmt.Println(string(buf))
|
||||
},
|
||||
}
|
||||
rootCmd.AddCommand(configCmd)
|
||||
|
||||
}
|
||||
+22
-73
@@ -2,85 +2,34 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/config"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/server"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/artalkjs/artalk/v2/server"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
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) {
|
||||
core.LoadCore(cfgFile, workDir)
|
||||
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")
|
||||
|
||||
fmt.Println(Banner)
|
||||
fmt.Print("-------------------------------\n\n")
|
||||
|
||||
// create fiber app
|
||||
// @see https://docs.gofiber.io/api/fiber#config
|
||||
app := fiber.New(fiber.Config{
|
||||
// @see https://github.com/gofiber/fiber/issues/426
|
||||
// @see https://github.com/gofiber/fiber/issues/185
|
||||
Immutable: true,
|
||||
|
||||
// TODO add config to set ProxyHeader
|
||||
ProxyHeader: "X-Forwarded-For",
|
||||
EnableIPValidation: true,
|
||||
})
|
||||
|
||||
// logger
|
||||
app.Use(logger.New(logger.Config{
|
||||
Format: "[${status}] ${method} ${path} ${latency} ${ip} ${reqHeader:X-Request-ID} ${referer} ${ua}\n",
|
||||
Output: io.Discard,
|
||||
Done: func(c *fiber.Ctx, logString []byte) {
|
||||
statusOK := c.Response().StatusCode() >= 200 && c.Response().StatusCode() <= 302
|
||||
if !statusOK {
|
||||
logrus.StandardLogger().WriterLevel(logrus.ErrorLevel).Write(logString)
|
||||
} else {
|
||||
logrus.StandardLogger().WriterLevel(logrus.DebugLevel).Write(logString)
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
// init router
|
||||
server.Init(app)
|
||||
|
||||
// graceful shutdown signal listen
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
fmt.Println("Gracefully shutting down...")
|
||||
_ = app.Shutdown()
|
||||
}()
|
||||
|
||||
// socket listen
|
||||
listenAddr := fmt.Sprintf("%s:%d", config.Instance.Host, config.Instance.Port)
|
||||
if config.Instance.SSL.Enabled {
|
||||
app.ListenTLS(listenAddr, config.Instance.SSL.CertPath, config.Instance.SSL.KeyPath)
|
||||
} else {
|
||||
app.Listen(listenAddr)
|
||||
}
|
||||
|
||||
// graceful shutdown tasks
|
||||
fmt.Println("Running cleanup tasks...")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
// 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
|
||||
}
|
||||
+46
-46
@@ -5,64 +5,64 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ArtalkJS/Artalk/internal/config"
|
||||
"github.com/ArtalkJS/Artalk/internal/core"
|
||||
"github.com/ArtalkJS/Artalk/internal/i18n"
|
||||
"github.com/artalkjs/artalk/v2/internal/config"
|
||||
"github.com/artalkjs/artalk/v2/internal/i18n"
|
||||
"github.com/artalkjs/artalk/v2/internal/log"
|
||||
"github.com/blang/semver"
|
||||
"github.com/rhysd/go-github-selfupdate/selfupdate"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var 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) {
|
||||
// loadCore()
|
||||
core.LoadConfOnly(cfgFile, workDir)
|
||||
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") + "...")
|
||||
|
||||
logrus.Info(i18n.T("Checking for updates") + "...")
|
||||
|
||||
latest, found, err := selfupdate.DetectLatest("ArtalkJS/Artalk")
|
||||
if err != nil {
|
||||
logrus.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) {
|
||||
logrus.Println(i18n.T("Current version is the latest") + " (v" + v.String() + ")")
|
||||
return
|
||||
latest, found, err := selfupdate.DetectLatest("ArtalkJS/Artalk")
|
||||
if err != nil {
|
||||
log.Fatal("Error occurred while detecting version: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info(i18n.T("New version available") + ": v" + latest.Version.String())
|
||||
logrus.Info(i18n.T("Downloading") + "...")
|
||||
ignoreVersionCheck, _ := cmd.Flags().GetBool("force")
|
||||
if !ignoreVersionCheck {
|
||||
v := semver.MustParse(strings.TrimPrefix(config.Version, "v"))
|
||||
if !found || latest.Version.LTE(v) {
|
||||
log.Info(i18n.T("Current version is the latest") + " (v" + v.String() + ")")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
logrus.Fatal("Could not locate executable path ", err)
|
||||
}
|
||||
log.Info(i18n.T("New version available") + ": v" + latest.Version.String())
|
||||
log.Info(i18n.T("Downloading") + "...")
|
||||
|
||||
if err := selfupdate.UpdateTo(latest.AssetURL, exe); err != nil {
|
||||
logrus.Fatal(i18n.T("Update failed")+" ", err)
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatal("Could not locate executable path ", err)
|
||||
}
|
||||
|
||||
logrus.Println(i18n.T("Update complete"))
|
||||
fmt.Println("\n-------------------------------\n v" +
|
||||
latest.Version.String() +
|
||||
" Release Note\n" +
|
||||
"-------------------------------\n\n" +
|
||||
latest.ReleaseNotes)
|
||||
},
|
||||
}
|
||||
if err := selfupdate.UpdateTo(latest.AssetURL, exe); err != nil {
|
||||
log.Fatal(i18n.T("Update failed")+" ", err)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(upgradeCmd)
|
||||
log.Info(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
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
//// 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,23 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/artalkjs/artalk/v2/internal/config"
|
||||
"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 (" + config.VersionString() + ")")
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
BootModeKey: MODE_MINI_BOOT,
|
||||
},
|
||||
}
|
||||
|
||||
return versionCmd
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ignore:
|
||||
- "docs/*"
|
||||
+131
-44
@@ -2,30 +2,37 @@ host: "0.0.0.0"
|
||||
port: 23366
|
||||
app_key: ""
|
||||
debug: false
|
||||
locale: "en"
|
||||
timezone: "Asia/Shanghai"
|
||||
site_default: "Default Site"
|
||||
locale: en
|
||||
timezone: Asia/Shanghai
|
||||
site_default: Default Site
|
||||
site_url: ""
|
||||
login_timeout: 259200
|
||||
db:
|
||||
type: "sqlite"
|
||||
file: "./data/artalk.db"
|
||||
type: sqlite
|
||||
file: ./data/artalk.db
|
||||
table_prefix: ""
|
||||
name: "artalk"
|
||||
host: "localhost"
|
||||
name: artalk
|
||||
host: localhost
|
||||
port: 3306
|
||||
user: "root"
|
||||
user: root
|
||||
password: ""
|
||||
charset: "utf8mb4"
|
||||
charset: utf8mb4
|
||||
ssl: false
|
||||
prepare_stmt: true
|
||||
http:
|
||||
body_limit: 100
|
||||
proxy_header: ""
|
||||
log:
|
||||
enabled: true
|
||||
filename: "./data/artalk.log"
|
||||
filename: ./data/artalk.log
|
||||
cache:
|
||||
type: "builtin"
|
||||
enabled: false
|
||||
type: builtin
|
||||
expires: 30
|
||||
warm_up: false
|
||||
server: ""
|
||||
redis:
|
||||
network: "tcp"
|
||||
network: tcp
|
||||
username: ""
|
||||
password: ""
|
||||
db: 0
|
||||
@@ -34,7 +41,6 @@ ssl:
|
||||
enabled: false
|
||||
cert_path: ""
|
||||
key_path: ""
|
||||
admin_users:
|
||||
moderator:
|
||||
pending_default: false
|
||||
api_fail_block: false
|
||||
@@ -43,22 +49,23 @@ moderator:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
region: ap-guangzhou
|
||||
aliyun:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
region: cn-shanghai
|
||||
keywords:
|
||||
enabled: false
|
||||
pending: false
|
||||
files: []
|
||||
files:
|
||||
- ./data/keywords_1.txt
|
||||
file_sep: "\n"
|
||||
replac_to: "x"
|
||||
replace_to: x
|
||||
captcha:
|
||||
enabled: true
|
||||
always: false
|
||||
captcha_type: "image"
|
||||
captcha_type: image
|
||||
action_limit: 3
|
||||
action_reset: 60
|
||||
turnstile:
|
||||
@@ -73,32 +80,34 @@ captcha:
|
||||
geetest:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
email:
|
||||
enabled: false
|
||||
send_type: "smtp"
|
||||
send_name: "{{reply_nick}}"
|
||||
send_addr: "noreply@example.com"
|
||||
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"
|
||||
img_upload:
|
||||
enabled: true
|
||||
path: "./data/artalk-img/"
|
||||
path: ./data/artalk-img/
|
||||
max_size: 5
|
||||
public_path: null
|
||||
upgit:
|
||||
enabled: false
|
||||
exec: "./upgit -c UPGIT_CONF_FILE_PATH -t /artalk-img"
|
||||
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"
|
||||
notify_tpl: default
|
||||
notify_pending: false
|
||||
noise_mode: false
|
||||
email:
|
||||
enabled: true
|
||||
@@ -111,10 +120,11 @@ admin_notify:
|
||||
- 7777777
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
server: http://day.app/xxxxxxx/
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
msg_type: "text"
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
@@ -134,25 +144,100 @@ admin_notify:
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
auth:
|
||||
enabled: false
|
||||
anonymous: false
|
||||
callback: "http://localhost:23366/api/v2/auth/{provider}/callback"
|
||||
email:
|
||||
enabled: true
|
||||
verify_subject: "Your Code - {{code}}"
|
||||
verify_tpl: default
|
||||
github:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
gitlab:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
gitea:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
google:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
mastodon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
twitter:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
facebook:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
discord:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
steam:
|
||||
enabled: false
|
||||
api_key: ""
|
||||
apple:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
microsoft:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
wechat:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
tiktok:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
slack:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
line:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
patreon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
auth0:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
domain: ""
|
||||
frontend:
|
||||
placeholder: ""
|
||||
noComment: ""
|
||||
sendBtn: ""
|
||||
editorTravel: true
|
||||
emoticons: ""
|
||||
emoticons: https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json
|
||||
vote: true
|
||||
voteDown: false
|
||||
uaBadge: true
|
||||
uaBadge: false
|
||||
listSort: true
|
||||
pvEl: "#ArtalkPV"
|
||||
countEl: "#ArtalkCount"
|
||||
preview: true
|
||||
flatMode: "auto"
|
||||
flatMode: auto
|
||||
darkMode: inherit
|
||||
nestMax: 2
|
||||
nestSort: DATE_ASC
|
||||
gravatar:
|
||||
mirror: "https://www.gravatar.com/"
|
||||
default: "mp"
|
||||
mirror: https://www.gravatar.com/avatar/
|
||||
params: sha256=1&d=mp&s=240
|
||||
pagination:
|
||||
pageSize: 20
|
||||
readMore: true
|
||||
@@ -161,5 +246,7 @@ frontend:
|
||||
content: 300
|
||||
children: 400
|
||||
scrollable: false
|
||||
imgLazyLoad: false
|
||||
reqTimeout: 15000
|
||||
versionCheck: true
|
||||
pluginURLs: []
|
||||
+168
-40
@@ -10,16 +10,19 @@ app_key: ""
|
||||
# Debug mode
|
||||
debug: false
|
||||
|
||||
# Language (follow Unicode BCP 47) ["en", "zh-CN", "zh-TW", "jp"]
|
||||
# Language (follow Unicode BCP 47) ["en", "zh-CN", "zh-TW", "ja", "fr", "ko", "ru"]
|
||||
# -- see https://www.techonthenet.com/js/language_tags.php --
|
||||
locale: "en"
|
||||
locale: en
|
||||
|
||||
# Timezone (follow IANA Time Zone Database)
|
||||
# -- see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones --
|
||||
timezone: "Asia/Shanghai"
|
||||
timezone: Asia/Shanghai
|
||||
|
||||
# Default site name (create when app is first launched)
|
||||
site_default: "Default Site"
|
||||
site_default: Default Site
|
||||
|
||||
# Default site url
|
||||
site_url: ""
|
||||
|
||||
# Login timeout (in seconds)
|
||||
login_timeout: 259200
|
||||
@@ -27,36 +30,49 @@ login_timeout: 259200
|
||||
# Database
|
||||
db:
|
||||
# Database type ["sqlite", "mysql", "pgsql", "mssql"]
|
||||
type: "sqlite"
|
||||
type: sqlite
|
||||
# Database file (only for SQLite)
|
||||
file: "./data/artalk.db"
|
||||
file: ./data/artalk.db
|
||||
# Table prefix (e.g. "atk_")
|
||||
table_prefix: ""
|
||||
# -- The following is not necessary for SQLite --
|
||||
# Database name
|
||||
name: "artalk"
|
||||
name: artalk
|
||||
# Host address
|
||||
host: "localhost"
|
||||
host: localhost
|
||||
# Host port
|
||||
port: 3306
|
||||
# Database user
|
||||
user: "root"
|
||||
user: root
|
||||
# Database password
|
||||
password: ""
|
||||
# Database charset
|
||||
charset: "utf8mb4"
|
||||
charset: utf8mb4
|
||||
# Enable SSL mode
|
||||
ssl: false
|
||||
# Prepared Statement
|
||||
prepare_stmt: true
|
||||
|
||||
# Web server
|
||||
http:
|
||||
# Body size limit (unit: MB)
|
||||
body_limit: 100
|
||||
# Proxy Header (fill `X-Forwarded-For` to get user real IP if behind a trusted reverse proxy or CDN)
|
||||
proxy_header: ""
|
||||
|
||||
# Logging
|
||||
log:
|
||||
# Enable logging
|
||||
enabled: true
|
||||
# Log file path
|
||||
filename: "./data/artalk.log"
|
||||
filename: ./data/artalk.log
|
||||
|
||||
# Cache
|
||||
cache:
|
||||
# Enable cache
|
||||
enabled: false
|
||||
# Cache type ["redis", "memcache", "builtin"]
|
||||
type: "builtin"
|
||||
type: builtin
|
||||
# Cache expiration time (in minutes)
|
||||
expires: 30
|
||||
# Cache warm up (warm up cache when program starts)
|
||||
@@ -67,7 +83,7 @@ cache:
|
||||
# Redis config
|
||||
redis:
|
||||
# Connection type ["tcp", "unix"]
|
||||
network: "tcp"
|
||||
network: tcp
|
||||
# Redis username
|
||||
username: ""
|
||||
# Redis password
|
||||
@@ -107,7 +123,7 @@ moderator:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
region: ap-guangzhou
|
||||
# Aliyun Content Security
|
||||
# (Auto review comments with Aliyun Content Security)
|
||||
# -- see https://help.aliyun.com/document_detail/28417.html --
|
||||
@@ -115,7 +131,7 @@ moderator:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
region: cn-shanghai
|
||||
# Keyword filter (local offline dictionary)
|
||||
keywords:
|
||||
# Enable keyword filter
|
||||
@@ -124,9 +140,9 @@ moderator:
|
||||
pending: false
|
||||
# Dictionary file (support multiple dictionary files)
|
||||
files:
|
||||
- "./data/keywords_1.txt"
|
||||
- ./data/keywords_1.txt
|
||||
file_sep: "\n"
|
||||
replac_to: "x"
|
||||
replace_to: x
|
||||
|
||||
# Captcha
|
||||
captcha:
|
||||
@@ -135,7 +151,7 @@ captcha:
|
||||
# Captcha is required always
|
||||
always: false
|
||||
# Captcha type ["image", "turnstile", "recaptcha", "hcaptcha", "geetest"]
|
||||
captcha_type: "image"
|
||||
captcha_type: image
|
||||
# Action limit
|
||||
# (the number of actions required to activate captcha)
|
||||
action_limit: 3
|
||||
@@ -165,7 +181,7 @@ img_upload:
|
||||
# Enable image upload
|
||||
enabled: true
|
||||
# Image storage
|
||||
path: "./data/artalk-img/"
|
||||
path: ./data/artalk-img/
|
||||
# Image size limit (unit: MB)
|
||||
max_size: 5
|
||||
# Image link base path (default: "/static/images/")
|
||||
@@ -175,7 +191,7 @@ img_upload:
|
||||
# Enable Upgit
|
||||
enabled: false
|
||||
# Command line arguments
|
||||
exec: "./upgit -c UPGIT_CONF_FILE_PATH -t /artalk-img"
|
||||
exec: upgit -c UPGIT_CONF_FILE_PATH -t /artalk-img
|
||||
# Delete local image after upload success
|
||||
del_local: true
|
||||
|
||||
@@ -184,23 +200,23 @@ email:
|
||||
# Enable email notification
|
||||
enabled: false
|
||||
# Send method ["smtp", "ali_dm", "sendmail"]
|
||||
send_type: "smtp"
|
||||
send_type: smtp
|
||||
# Nick name of sender
|
||||
send_name: "{{reply_nick}}"
|
||||
# Email address of sender
|
||||
send_addr: "noreply@example.com"
|
||||
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"
|
||||
mail_tpl: default
|
||||
# SMTP send (set send method to "smtp" to enable)
|
||||
smtp:
|
||||
# Email address of sender
|
||||
host: "smtp.qq.com"
|
||||
host: smtp.qq.com
|
||||
# Email port
|
||||
port: 587
|
||||
# Email address of sender
|
||||
username: "example@qq.com"
|
||||
username: example@qq.com
|
||||
# Password
|
||||
password: ""
|
||||
# Aliyun mail push
|
||||
@@ -208,12 +224,14 @@ email:
|
||||
ali_dm:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
account_name: "noreply@example.com"
|
||||
account_name: noreply@example.com
|
||||
|
||||
# Multi-push
|
||||
# Multi-Push
|
||||
admin_notify:
|
||||
# Notification template (set to file path to use custom template)
|
||||
notify_tpl: "default"
|
||||
notify_tpl: default
|
||||
# Pending comment still send notification (notifications are still sent when comments are intercepted)
|
||||
notify_pending: false
|
||||
# Noise mode
|
||||
# -- noise_mode is disabled by default. --
|
||||
# -- When this option is set to `false`, only messages sent to the administrator will be notified, --
|
||||
@@ -236,14 +254,18 @@ admin_notify:
|
||||
# Bark
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
server: http://day.app/xxxxxxx/
|
||||
# Lark
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
# Message type ["text", "card"]
|
||||
msg_type: "text"
|
||||
# WebHook
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
# DingTalk
|
||||
ding_talk:
|
||||
enabled: false
|
||||
token: ""
|
||||
@@ -260,8 +282,110 @@ admin_notify:
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
- USER_ID_1
|
||||
- GROUP_ID_1
|
||||
|
||||
# Social Login
|
||||
auth:
|
||||
# Enable Social Login
|
||||
enabled: false
|
||||
# Allow anonymous commenting (Allow skipping verification, only fill in an anonymous nickname and email)
|
||||
anonymous: false
|
||||
# Callback URL (https://example.com/api/v2/auth/{provider}/callback)
|
||||
callback: "http://localhost:23366/api/v2/auth/{provider}/callback"
|
||||
# Email
|
||||
email:
|
||||
# Enable email password login
|
||||
enabled: true
|
||||
# Verification email subject
|
||||
verify_subject: "Your Code - {{code}}"
|
||||
# Verification email template (set to file path to use custom template)
|
||||
verify_tpl: default
|
||||
# GitHub
|
||||
github:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# GitLab
|
||||
gitlab:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Gitea
|
||||
gitea:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Google
|
||||
google:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Mastodon
|
||||
mastodon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Twitter
|
||||
twitter:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Facebook
|
||||
facebook:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Discord
|
||||
discord:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Steam
|
||||
steam:
|
||||
enabled: false
|
||||
api_key: ""
|
||||
# Apple
|
||||
apple:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Microsoft
|
||||
microsoft:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# WeChat
|
||||
wechat:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Tiktok
|
||||
tiktok:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Slack
|
||||
slack:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Line
|
||||
line:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Patreon
|
||||
patreon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Auth0
|
||||
auth0:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
domain: ""
|
||||
|
||||
# UI Settings
|
||||
frontend:
|
||||
@@ -274,31 +398,31 @@ frontend:
|
||||
# Movable comment box
|
||||
editorTravel: true
|
||||
# Emoticons
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
emoticons: https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json
|
||||
# Vote button
|
||||
vote: true
|
||||
# Dislike button
|
||||
voteDown: false
|
||||
# User UA badge
|
||||
uaBadge: true
|
||||
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"
|
||||
flatMode: auto
|
||||
# Dark mode ["inherit", "auto"]
|
||||
darkMode: inherit
|
||||
# Maximum nesting level
|
||||
nestMax: 2
|
||||
# Nesting comment sorting rules ["DATE_ASC", "DATE_DESC", "VOTE_UP_DESC"]
|
||||
nestSort: DATE_ASC
|
||||
# Gravatar
|
||||
gravatar:
|
||||
mirror: "https://www.gravatar.com/"
|
||||
default: "mp"
|
||||
# API URL
|
||||
mirror: https://www.gravatar.com/avatar/
|
||||
# API parameters
|
||||
params: sha256=1&d=mp&s=240
|
||||
# Comment pagination
|
||||
pagination:
|
||||
# Number of comments per page
|
||||
@@ -315,7 +439,11 @@ frontend:
|
||||
children: 400
|
||||
# Scrollable (scrollable height limit area)
|
||||
scrollable: false
|
||||
# Image lazy load [false, "native", "data-src"]
|
||||
imgLazyLoad: false
|
||||
# Request timeout (unit: ms)
|
||||
reqTimeout: 15000
|
||||
# Version check
|
||||
versionCheck: true
|
||||
# Plugins
|
||||
pluginURLs: []
|
||||
+183
-56
@@ -10,50 +10,66 @@ app_key: ""
|
||||
# 调试模式
|
||||
debug: false
|
||||
|
||||
# 语言 ["en", "zh-CN", "zh-TW", "jp"]
|
||||
locale: "zh-CN"
|
||||
# 语言 ["en", "zh-CN", "zh-TW", "ja", "fr", "ko", "ru"]
|
||||
locale: zh-CN
|
||||
|
||||
# 时间区域
|
||||
timezone: "Asia/Shanghai"
|
||||
timezone: Asia/Shanghai
|
||||
|
||||
# 默认站点名
|
||||
site_default: "默认站点"
|
||||
site_default: 默认站点
|
||||
|
||||
# 登陆有效时长 (单位:秒)
|
||||
# 默认站点地址
|
||||
site_url: ""
|
||||
|
||||
# 登录有效时长 (单位:秒)
|
||||
login_timeout: 259200
|
||||
|
||||
# 数据库
|
||||
db:
|
||||
# 数据库类型 ["sqlite", "mysql", "pgsql", "mssql"]
|
||||
type: "sqlite"
|
||||
type: sqlite
|
||||
# 数据库文件 (仅 SQLite 数据库需填写)
|
||||
file: "./data/artalk.db"
|
||||
file: ./data/artalk.db
|
||||
# 数据库名称
|
||||
name: "artalk"
|
||||
name: artalk
|
||||
# 数据库地址
|
||||
host: "localhost"
|
||||
host: localhost
|
||||
# 数据库端口
|
||||
port: 3306
|
||||
# 数据库账户
|
||||
user: "root"
|
||||
user: root
|
||||
# 数据库密码
|
||||
password: ""
|
||||
# 编码格式
|
||||
charset: "utf8mb4"
|
||||
charset: utf8mb4
|
||||
# 表前缀 (例如:"atk_")
|
||||
table_prefix: ""
|
||||
# 启用 SSL
|
||||
ssl: false
|
||||
# 预编译语句
|
||||
prepare_stmt: true
|
||||
|
||||
# 服务器
|
||||
http:
|
||||
# 请求体大小限制 (单位:MB)
|
||||
body_limit: 100
|
||||
# 代理标头名 (当使用 CDN 时填写 `X-Forwarded-For` 获取用户真实 IP)
|
||||
proxy_header: ""
|
||||
|
||||
# 日志
|
||||
log:
|
||||
# 启用日志
|
||||
enabled: true
|
||||
# 日志文件路径
|
||||
filename: "./data/artalk.log"
|
||||
filename: ./data/artalk.log
|
||||
|
||||
# 缓存
|
||||
cache:
|
||||
# 启用缓存
|
||||
enabled: false
|
||||
# 缓存类型 ["redis", "memcache", "builtin"]
|
||||
type: "builtin"
|
||||
type: builtin
|
||||
# 缓存过期时间 (单位:分钟)
|
||||
expires: 30
|
||||
# 缓存启动预热 (程序启动时预热缓存)
|
||||
@@ -63,7 +79,7 @@ cache:
|
||||
# Redis 配置
|
||||
redis:
|
||||
# 连接方式 ["tcp", "unix"]
|
||||
network: "tcp"
|
||||
network: tcp
|
||||
# 用户名
|
||||
username: ""
|
||||
# 密码
|
||||
@@ -72,7 +88,8 @@ cache:
|
||||
db: 0
|
||||
|
||||
# 可信域名
|
||||
trusted_domains: [] # 例如:["https://artalk.example.com:23366"]
|
||||
# -- 例如:["https://artalk.example.com:23366"] --
|
||||
trusted_domains: []
|
||||
|
||||
# SSL
|
||||
ssl:
|
||||
@@ -84,12 +101,14 @@ ssl:
|
||||
key_path: ""
|
||||
|
||||
# 管理员账户
|
||||
admin_users:
|
||||
# - name: "admin"
|
||||
# email: "admin@example.com"
|
||||
# password: "" # 支持 bcrypt 或 md5 加密,如:"(md5)50c21190c6e4e5418c6a90d2b5031119"
|
||||
# badge_name: "管理员"
|
||||
# badge_color: "#0083FF"
|
||||
# --
|
||||
# - name: "admin"
|
||||
# email: "admin@example.com"
|
||||
# password: "" # 支持 bcrypt 或 md5 加密,如:"(bcrypt)$2y$10$ti4vZYIrxVN8rLcYXVgXCO.GJND0dyI49r7IoF3xqIx8bBRmIBZRm"
|
||||
# badge_name: "管理员"
|
||||
# badge_color: "#0083FF"
|
||||
# --
|
||||
admin_users: []
|
||||
|
||||
# 评论审核
|
||||
moderator:
|
||||
@@ -106,14 +125,14 @@ moderator:
|
||||
enabled: false
|
||||
secret_id: ""
|
||||
secret_key: ""
|
||||
region: "ap-guangzhou"
|
||||
region: ap-guangzhou
|
||||
# 阿里云内容安全
|
||||
# (https://help.aliyun.com/document_detail/28417.html)
|
||||
aliyun:
|
||||
enabled: false
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
region: "cn-shanghai"
|
||||
region: cn-shanghai
|
||||
# 关键词过滤 (本地离线词库)
|
||||
keywords:
|
||||
enabled: false
|
||||
@@ -121,11 +140,11 @@ moderator:
|
||||
pending: false
|
||||
# 词库文件 (支持多个词库文件)
|
||||
files:
|
||||
- "./data/词库_1.txt"
|
||||
- ./data/词库_1.txt
|
||||
# 词库文件内容分割符 (例如填写 "\n" 文件中一行一个关键词)
|
||||
file_sep: "\n"
|
||||
# 替换字符
|
||||
replac_to: "x"
|
||||
replace_to: x
|
||||
|
||||
# 验证码
|
||||
captcha:
|
||||
@@ -134,7 +153,7 @@ captcha:
|
||||
# 总是需要验证码
|
||||
always: false
|
||||
# 验证类型 ["image", "turnstile", "recaptcha", "hcaptcha", "geetest"]
|
||||
captcha_type: "image"
|
||||
captcha_type: image
|
||||
# 激活验证码所需操作次数
|
||||
action_limit: 3
|
||||
# 重置操作计数器超时 (单位:s, 设为 -1 不重置)
|
||||
@@ -158,21 +177,21 @@ captcha:
|
||||
captcha_id: ""
|
||||
captcha_key: ""
|
||||
|
||||
# IP 归属地
|
||||
# IP 属地
|
||||
ip_region:
|
||||
# 启用 IP 归属地展示
|
||||
# 启用 IP 属地展示
|
||||
enabled: false
|
||||
# 数据文件路径 (.xdb 格式)
|
||||
db_path: "./data/ip2region.xdb"
|
||||
db_path: ./data/ip2region.xdb
|
||||
# 显示精度 ["province", "city", "country"]
|
||||
precision: "province"
|
||||
precision: province
|
||||
|
||||
# 图片上传
|
||||
img_upload:
|
||||
# 启用图片上传
|
||||
enabled: true
|
||||
# 图片存放路径
|
||||
path: "./data/artalk-img/"
|
||||
path: ./data/artalk-img/
|
||||
# 图片大小限制 (单位:MB)
|
||||
max_size: 5
|
||||
# 图片链接基础路径 (默认为 "/static/images/")
|
||||
@@ -183,7 +202,7 @@ img_upload:
|
||||
# 启用 Upgit
|
||||
enabled: false
|
||||
# 命令行参数
|
||||
exec: "./upgit -c <upgit配置文件路径> -t /artalk-img"
|
||||
exec: upgit -c <upgit配置文件路径> -t /artalk-img
|
||||
# 上传后删除本地的图片
|
||||
del_local: true
|
||||
|
||||
@@ -192,23 +211,23 @@ email:
|
||||
# 启用邮件通知
|
||||
enabled: false
|
||||
# 发送方式 ["smtp", "ali_dm", "sendmail"]
|
||||
send_type: "smtp"
|
||||
send_type: smtp
|
||||
# 发信人昵称
|
||||
send_name: "{{reply_nick}}"
|
||||
# 发信人地址
|
||||
send_addr: "noreply@example.com"
|
||||
send_addr: noreply@example.com
|
||||
# 邮件标题
|
||||
mail_subject: "[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复"
|
||||
# 邮件模板文件 (填入文件路径使用自定义模板)
|
||||
mail_tpl: "default"
|
||||
mail_tpl: default
|
||||
# SMTP 发送 (启用请将发送方式设为 "smtp")
|
||||
smtp:
|
||||
# 发件地址
|
||||
host: "smtp.qq.com"
|
||||
host: smtp.qq.com
|
||||
# 发件端口
|
||||
port: 587
|
||||
# 用户名
|
||||
username: "example@qq.com"
|
||||
username: example@qq.com
|
||||
# 密码
|
||||
password: ""
|
||||
# 阿里云邮件推送
|
||||
@@ -216,12 +235,14 @@ email:
|
||||
ali_dm:
|
||||
access_key_id: ""
|
||||
access_key_secret: ""
|
||||
account_name: "noreply@example.com"
|
||||
account_name: noreply@example.com
|
||||
|
||||
# 多元推送
|
||||
admin_notify:
|
||||
# 通知模版 (填入文件路径使用自定义模板)
|
||||
notify_tpl: "default"
|
||||
notify_tpl: default
|
||||
# 待审评论仍然发送通知 (当评论被拦截时仍然发送通知)
|
||||
notify_pending: false
|
||||
# 嘈杂模式
|
||||
noise_mode: false
|
||||
# 邮件通知管理员
|
||||
@@ -241,11 +262,13 @@ admin_notify:
|
||||
# Bark
|
||||
bark:
|
||||
enabled: false
|
||||
server: "http://day.app/xxxxxxx/"
|
||||
server: http://day.app/xxxxxxx/
|
||||
# 飞书
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ""
|
||||
# 消息类型 ["text", "card"]
|
||||
msg_type: "text"
|
||||
# WebHook
|
||||
webhook:
|
||||
enabled: false
|
||||
@@ -260,17 +283,119 @@ admin_notify:
|
||||
enabled: false
|
||||
oauth_token: ""
|
||||
receivers:
|
||||
- "CHANNEL_ID"
|
||||
- CHANNEL_ID
|
||||
# LINE
|
||||
line:
|
||||
enabled: false
|
||||
channel_secret: ""
|
||||
channel_access_token: ""
|
||||
receivers:
|
||||
- "USER_ID_1"
|
||||
- "GROUP_ID_1"
|
||||
- USER_ID_1
|
||||
- GROUP_ID_1
|
||||
|
||||
# 前端配置
|
||||
# 社交登录
|
||||
auth:
|
||||
# 启用社交登录
|
||||
enabled: false
|
||||
# 允许匿名评论 (允许跳过验证,仅填写匿名的昵称和邮箱)
|
||||
anonymous: false
|
||||
# 回调地址 (https://example.com/api/v2/auth/{provider}/callback)
|
||||
callback: "http://localhost:23366/api/v2/auth/{provider}/callback"
|
||||
# Email
|
||||
email:
|
||||
# 启用邮箱密码登录
|
||||
enabled: true
|
||||
# 邮箱验证邮件标题
|
||||
verify_subject: "您的验证码是 - {{code}}"
|
||||
# 邮箱验证邮件模板 (填入文件路径使用自定义模板)
|
||||
verify_tpl: default
|
||||
# GitHub
|
||||
github:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# GitLab
|
||||
gitlab:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Gitea
|
||||
gitea:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Google
|
||||
google:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Mastodon
|
||||
mastodon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Twitter
|
||||
twitter:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Facebook
|
||||
facebook:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Discord
|
||||
discord:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Steam
|
||||
steam:
|
||||
enabled: false
|
||||
api_key: ""
|
||||
# Apple
|
||||
apple:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Microsoft
|
||||
microsoft:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# 微信
|
||||
wechat:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Tiktok
|
||||
tiktok:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Slack
|
||||
slack:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Line
|
||||
line:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Patreon
|
||||
patreon:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
# Auth0
|
||||
auth0:
|
||||
enabled: false
|
||||
client_id: ""
|
||||
client_secret: ""
|
||||
domain: ""
|
||||
|
||||
# 界面配置
|
||||
frontend:
|
||||
# 评论框占位文字
|
||||
placeholder: ""
|
||||
@@ -281,33 +406,31 @@ frontend:
|
||||
# 评论框穿梭
|
||||
editorTravel: true
|
||||
# 表情包
|
||||
emoticons: "https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json"
|
||||
emoticons: https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json
|
||||
# 投票按钮
|
||||
vote: true
|
||||
# 反对按钮
|
||||
voteDown: false
|
||||
# 用户 UA 徽标
|
||||
uaBadge: true
|
||||
uaBadge: false
|
||||
# 评论排序功能
|
||||
listSort: true
|
||||
# 页面 PV 绑定元素
|
||||
pvEl: "#ArtalkPV"
|
||||
# 评论数绑定元素
|
||||
countEl: "#ArtalkCount"
|
||||
# 编辑器实时预览功能
|
||||
preview: true
|
||||
# 平铺模式 ["auto", true, false]
|
||||
flatMode: "auto"
|
||||
flatMode: auto
|
||||
# 夜间模式 ["inherit", "auto"]
|
||||
darkMode: inherit
|
||||
# 最大嵌套层数
|
||||
nestMax: 2
|
||||
# 嵌套评论排序规则 ["DATE_ASC", "DATE_DESC", "VOTE_UP_DESC"]
|
||||
nestSort: DATE_ASC
|
||||
# 头像
|
||||
# 头像 Gravatar
|
||||
gravatar:
|
||||
# Gravatar 镜像地址
|
||||
mirror: "https://cravatar.cn/avatar/"
|
||||
# 默认头像
|
||||
default: "mp"
|
||||
# API 地址
|
||||
mirror: https://weavatar.com/avatar/
|
||||
# API 参数
|
||||
params: sha256=1&d=mp&s=240
|
||||
# 评论分页
|
||||
pagination:
|
||||
# 每页评论数
|
||||
@@ -324,7 +447,11 @@ frontend:
|
||||
children: 400
|
||||
# 滚动限高 (允许限高区域滚动)
|
||||
scrollable: false
|
||||
# 图片懒加载 [false, "native", "data-src"]
|
||||
imgLazyLoad: false
|
||||
# 请求超时 (单位:毫秒)
|
||||
reqTimeout: 15000
|
||||
# 版本检测
|
||||
versionCheck: true
|
||||
# 插件
|
||||
pluginURLs: []
|
||||
+7
-1
@@ -1,8 +1,9 @@
|
||||
version: "3.5"
|
||||
version: "3.8"
|
||||
services:
|
||||
artalk:
|
||||
container_name: artalk
|
||||
image: artalk/artalk-go
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
@@ -10,3 +11,8 @@ services:
|
||||
- 8080:23366
|
||||
volumes:
|
||||
- ./data:/data
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- ATK_LOCALE=zh-CN
|
||||
- ATK_SITE_DEFAULT=Artalk 的博客
|
||||
- ATK_SITE_URL=https://example.com
|
||||
@@ -14,11 +14,11 @@ if [ "$1" != 'gen' ] && ( [ ! -e /data/artalk.yml ] && [ ! -e /data/artalk-go.ym
|
||||
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"
|
||||
echo "$(date) [info][docker] 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'"
|
||||
echo "$(date) [info][docker] Generate new config file to '/data/artalk.yml'"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
/.vitepress/dist/
|
||||
/.vitepress/cache/
|
||||
/public/http-api.html
|
||||
@@ -1 +0,0 @@
|
||||
shamefully-hoist=true
|
||||
@@ -1,206 +0,0 @@
|
||||
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: '/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: '开发文档', 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: 'Docker', link: '/guide/backend/docker.md' },
|
||||
{ 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: '更多内容',
|
||||
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' }
|
||||
],
|
||||
|
||||
algolia: {
|
||||
appId: 'BH4D9OD16A',
|
||||
apiKey: '37ab96e3f5a774cbbf0571b035b42adb',
|
||||
indexName: 'artalk-js',
|
||||
searchParameters: {
|
||||
facetFilters: ['lang:zh-CN']
|
||||
}
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/ArtalkJS/Artalk/edit/master/docs/:path',
|
||||
text: '完善文档'
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<div ref="artalkEl" style="margin-top: 20px;"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, nextTick, ref, onMounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
|
||||
const artalkEl = ref<HTMLElement | null>(null)
|
||||
|
||||
const router = useRouter()
|
||||
const page = useData().page
|
||||
|
||||
onMounted(() => {
|
||||
const script = document.createElement('script')
|
||||
script.src = `https://artalk.qwqaq.com/dist/Artalk.js`
|
||||
document.getElementsByTagName('head')[0].appendChild(script)
|
||||
script.onload = () => {
|
||||
initArtalk(page.value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => router.route.data.relativePath, (path) => {
|
||||
nextTick(() => {
|
||||
Artalk.update(getArtalkConfByPage(page.value))
|
||||
Artalk.reload()
|
||||
})
|
||||
})
|
||||
|
||||
function getArtalkConfByPage(page: any) {
|
||||
return {
|
||||
pageKey: 'https://artalk.js.org'+location.pathname,
|
||||
pageTitle: page.title,
|
||||
server: 'https://artalk.qwqaq.com',
|
||||
site: 'ArtalkDocs',
|
||||
}
|
||||
}
|
||||
|
||||
function initArtalk(page: any) {
|
||||
Artalk.init({
|
||||
el: artalkEl.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
gravatar: {
|
||||
mirror: 'https://cravatar.cn/avatar/'
|
||||
},
|
||||
...getArtalkConfByPage(page)
|
||||
})
|
||||
|
||||
// 图片灯箱插件
|
||||
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>
|
||||
@@ -1,18 +0,0 @@
|
||||
<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
-5
@@ -1,5 +0,0 @@
|
||||
declare module '*.vue' {
|
||||
import { defineComponent } from 'vue';
|
||||
const component: ReturnType<typeof defineComponent>;
|
||||
export default component;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"latest": "2.4.4"
|
||||
}
|
||||
@@ -1,592 +0,0 @@
|
||||
# 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 | 否 | 导入数据 |
|
||||
|
||||
**响应** 无
|
||||
@@ -1,36 +0,0 @@
|
||||
# Event
|
||||
|
||||
## 基本事件
|
||||
|
||||
|事件名|描述|
|
||||
|-|-|
|
||||
| `list-load` | 评论加载事件 |
|
||||
| `list-loaded` | 评论加载完成事件 |
|
||||
| `list-inserted` | 新增评论插入事件 |
|
||||
| `editor-submit` | 编辑器提交事件 |
|
||||
| `editor-submitted` | 编辑器提交完成事件 |
|
||||
| `user-changed` | 本地用户数据变更事件 |
|
||||
| `conf-loaded` | 配置变更事件 |
|
||||
| `sidebar-show` | 侧边栏显示事件 |
|
||||
| `sidebar-hide` | 侧边栏隐藏事件 |
|
||||
|
||||
## 添加事件监听
|
||||
|
||||
```js
|
||||
Artalk.use(ctx => {
|
||||
ctx.on('list-loaded', () => {
|
||||
alert('评论已加载完毕')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## 解除事件监听
|
||||
|
||||
```js
|
||||
let foo = function() { /* do something */ }
|
||||
|
||||
Artalk.use(ctx => {
|
||||
ctx.on('list-loaded', foo)
|
||||
ctx.off('list-loaded', foo)
|
||||
})
|
||||
```
|
||||
@@ -1,106 +0,0 @@
|
||||
# Frontend API
|
||||
|
||||
## 初始化 `Artalk.init`
|
||||
|
||||
创建 Artalk 实例化对象。
|
||||
|
||||
```js
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '/post/1',
|
||||
pageTitle: '关于引入 Artalk 的这档子事',
|
||||
server: 'http://your_domain:8080',
|
||||
site: 'Artalk 的博客',
|
||||
})
|
||||
```
|
||||
|
||||
## 更新配置 `Artalk.update`
|
||||
|
||||
修改 Artalk 当前配置。
|
||||
|
||||
```js
|
||||
Artalk.update({
|
||||
// 新的配置...
|
||||
})
|
||||
```
|
||||
|
||||
该函数调用后,`conf-loaded` 事件将被触发。
|
||||
|
||||
更新配置不会自动刷新评论列表,可按需继续执行 `Artalk.reload()` 函数。
|
||||
|
||||
注:前端的一些配置将会被后端的配置覆盖,详情见:[在后端控制前端](../guide/backend/fe-control.md)
|
||||
|
||||
## 重新加载 `Artalk.reload`
|
||||
|
||||
刷新 Artalk 评论列表。
|
||||
|
||||
```js
|
||||
Artalk.reload()
|
||||
```
|
||||
|
||||
列表加载前将触发 `list-load` 事件,加载完毕后将触发 `list-loaded` 事件。
|
||||
|
||||
## 释放资源 `Artalk.destroy`
|
||||
|
||||
销毁 Artalk 实例,用于资源释放。
|
||||
|
||||
```js
|
||||
Artalk.destroy()
|
||||
```
|
||||
|
||||
## 事件监听 `Artalk.on`
|
||||
|
||||
添加 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
Artalk.on('list-loaded', () => {
|
||||
alert('评论列表加载完毕')
|
||||
})
|
||||
```
|
||||
|
||||
可监听事件类型见:[前端界面事件](./event.md)
|
||||
|
||||
## 解除监听 `Artalk.off`
|
||||
|
||||
解除 Artalk 事件监听。
|
||||
|
||||
```js
|
||||
const handler = () => {
|
||||
alert('评论列表加载完毕')
|
||||
}
|
||||
|
||||
Artalk.on('list-loaded', handler)
|
||||
Artalk.off('list-loaded', handler)
|
||||
```
|
||||
|
||||
## 触发事件 `Artalk.trigger`
|
||||
|
||||
触发 Artalk 事件。
|
||||
|
||||
```js
|
||||
Artalk.trigger('list-loaded')
|
||||
```
|
||||
|
||||
## 扩展插件 `Artalk.use`
|
||||
|
||||
该 API 用于扩展 Artalk。
|
||||
|
||||
```js
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
})
|
||||
```
|
||||
|
||||
详情见:[插件开发](./plugs.md)
|
||||
|
||||
## 夜间模式 `Artalk.setDarkMode`
|
||||
|
||||
修改夜间模式,可以配合博客主题调用,例如当用户点击夜间模式切换按钮。
|
||||
|
||||
```js
|
||||
Artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
## 浏览量组件 `Artalk.loadCountWidget`
|
||||
|
||||
详情见:[浏览量统计](../guide/frontend/pv.md)
|
||||
@@ -1,28 +0,0 @@
|
||||
# 开发说明
|
||||
|
||||
## 参考资源
|
||||
|
||||
📖 <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.d.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/event.d.ts)
|
||||
|
||||
通过 Artalk 提供的 `API` 和 `Event`,你可以实现很多高级功能,比如编写评论管理机器人、评论提醒推送插件等。Artalk 并不为此提供技术指导,但鼓励你参考此处的文档自行定制。
|
||||
@@ -1,17 +0,0 @@
|
||||
# 插件开发
|
||||
|
||||
你可以使用 `Artalk.use` 来扩展 Artalk。
|
||||
|
||||
```js
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
})
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
::: warning
|
||||
Context API 目前仍不稳定,开发可能会有变动,升级请关注 CHANGELOG。
|
||||
:::
|
||||
|
||||
参考:[@artalk/types/context.d.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/packages/artalk/types/context.d.ts)
|
||||
@@ -0,0 +1,2 @@
|
||||
.vitepress/dist/**
|
||||
.vitepress/cache/**
|
||||
@@ -0,0 +1,125 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
export const en = defineConfig({
|
||||
lang: 'en-US',
|
||||
description: 'A Self-hosted Comment System.',
|
||||
|
||||
themeConfig: {
|
||||
sidebar: {
|
||||
'/en/guide/': [
|
||||
{
|
||||
text: 'Quick Start',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Introduction', link: '/en/guide/intro.md' },
|
||||
{ text: 'Getting Started', link: '/en/guide/deploy.md' },
|
||||
{ text: 'Data Migration', link: '/en/guide/transfer.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Core Guide',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Sidebar', link: '/en/guide/frontend/sidebar.md' },
|
||||
{ text: 'Email Notification', link: '/en/guide/backend/email.md' },
|
||||
{ text: 'Multi-channel Notification', link: '/en/guide/backend/admin_notify.md' },
|
||||
{ text: 'Social Login', link: '/en/guide/frontend/auth.md' },
|
||||
{ text: 'Comment Moderation', link: '/en/guide/backend/moderator.md' },
|
||||
{ text: 'Captcha', link: '/en/guide/backend/captcha.md' },
|
||||
{ text: 'Image Upload', link: '/en/guide/backend/img-upload.md' },
|
||||
{ text: 'Admins and Multi-Site', link: '/en/guide/backend/multi-site.md' },
|
||||
{ text: 'Resolve Relative Path', link: '/en/guide/backend/relative-path.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced Guide',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Emoticons', link: '/en/guide/frontend/emoticons.md' },
|
||||
{ text: 'Page View Statistics', link: '/en/guide/frontend/pv.md' },
|
||||
{ text: 'LaTeX', link: '/en/guide/frontend/latex.md' },
|
||||
{ text: 'Image Lightbox', link: '/en/guide/frontend/lightbox.md' },
|
||||
{ text: 'Image Lazy Load', link: '/en/guide/frontend/img-lazy-load.md' },
|
||||
{ text: 'IP Region', link: '/en/guide/frontend/ip-region.md' },
|
||||
{ text: 'Localization', link: '/en/guide/frontend/i18n.md' },
|
||||
{ text: 'Development Documentation', link: '/en/develop/index.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Configuration Documentation',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Environment Variables', link: '/en/guide/env.md' },
|
||||
{ text: 'Configuration File', link: '/en/guide/backend/config.md' },
|
||||
{ text: 'Interface Configuration', link: '/en/guide/frontend/config.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Deployment Instructions',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Daemon Process', link: '/en/guide/backend/daemon.md' },
|
||||
{ text: 'Reverse Proxy', link: '/en/guide/backend/reverse-proxy.md' },
|
||||
{
|
||||
text: 'Compile Source',
|
||||
link: 'https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md',
|
||||
},
|
||||
{ text: 'Program Upgrade', link: '/en/guide/backend/update.md' },
|
||||
{ text: 'Docker', link: '/en/guide/backend/docker.md' },
|
||||
],
|
||||
},
|
||||
],
|
||||
'/en/develop/': [
|
||||
{
|
||||
text: 'Development Documentation',
|
||||
items: [
|
||||
{ text: 'Development Instructions', link: '/en/develop/index.md' },
|
||||
{
|
||||
text: 'Contribution Guide',
|
||||
link: 'https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md',
|
||||
target: '_blank',
|
||||
},
|
||||
{ text: 'Import to Blog', link: '/en/develop/import-blog.md' },
|
||||
{ text: 'Import to Framework', link: '/en/develop/import-framework.md' },
|
||||
{ text: 'Frontend API', link: '/en/develop/fe-api.md' },
|
||||
{ text: 'Frontend Event', link: '/en/develop/event.md' },
|
||||
{ text: 'Plugin Development', link: '/en/develop/plugin.md' },
|
||||
{ text: 'Compatibility', link: '/en/develop/compatibility.md' },
|
||||
{
|
||||
text: 'HTTP API',
|
||||
link: 'https://artalk.js.org/http-api.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
nav: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
link: '/en/guide/intro',
|
||||
},
|
||||
{
|
||||
text: 'Deployment',
|
||||
link: '/en/guide/deploy',
|
||||
},
|
||||
{
|
||||
text: 'Configuration',
|
||||
link: '/en/guide/backend/config',
|
||||
},
|
||||
{
|
||||
text: 'Migration',
|
||||
link: '/en/guide/transfer',
|
||||
},
|
||||
{
|
||||
text: 'Development',
|
||||
link: '/en/develop/',
|
||||
},
|
||||
],
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/ArtalkJS/Artalk/edit/master/docs/docs/:path',
|
||||
text: 'Improve this document',
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { shared } from './shared'
|
||||
import { zh } from './zh'
|
||||
import { en } from './en'
|
||||
|
||||
export default defineConfig({
|
||||
...shared,
|
||||
locales: {
|
||||
en: { label: 'English', ...en },
|
||||
zh: { label: '简体中文', ...zh },
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import iterator from 'markdown-it-for-inline'
|
||||
import * as Version from '../../code/ArtalkVersion.json'
|
||||
|
||||
export const shared = defineConfig({
|
||||
title: 'Artalk',
|
||||
|
||||
/* prettier-ignore */
|
||||
head: [
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/favicon.png' }],
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:locale', content: 'en' }],
|
||||
['meta', { property: 'og:title', content: 'Artalk - A Self-hosted Comment System'}],
|
||||
['meta', { property: 'og:site_name', content: 'Artalk' }],
|
||||
['meta', { property: 'og:image', content: 'https://artalk.js.org/assets/images/artalk-banner.png' }],
|
||||
['meta', { property: 'og:url', content: 'https://artalk.js.org/' }],
|
||||
['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' }],
|
||||
],
|
||||
|
||||
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))
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
sitemap: {
|
||||
hostname: 'https://artalk.js.org',
|
||||
transformItems(items) {
|
||||
return items.filter((item) => !item.url.includes('migration'))
|
||||
},
|
||||
},
|
||||
|
||||
lastUpdated: true,
|
||||
cleanUrls: false,
|
||||
metaChunk: true,
|
||||
|
||||
themeConfig: {
|
||||
socialLinks: [{ icon: 'github', link: 'https://github.com/ArtalkJS/Artalk' }],
|
||||
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '2WNJ32WVTY',
|
||||
apiKey: '6c6ebc345a87b738264f19095b78c91c',
|
||||
indexName: 'artalk-js',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
server: {
|
||||
open: '/zh/guide/intro.html',
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,133 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
export const zh = defineConfig({
|
||||
lang: 'zh-CN',
|
||||
description: '由 Golang 驱动的自托管评论系统',
|
||||
|
||||
themeConfig: {
|
||||
sidebar: {
|
||||
'/zh/guide/': [
|
||||
{
|
||||
text: '快速开始',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '项目介绍', link: '/zh/guide/intro.md' },
|
||||
{ text: '程序部署', link: '/zh/guide/deploy.md' },
|
||||
{ text: '数据迁移', link: '/zh/guide/transfer.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '核心指南',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '侧边栏', link: '/zh/guide/frontend/sidebar.md' },
|
||||
{ text: '邮件通知', link: '/zh/guide/backend/email.md' },
|
||||
{ text: '多元推送', link: '/zh/guide/backend/admin_notify.md' },
|
||||
{ text: '社交登录', link: '/zh/guide/frontend/auth.md' },
|
||||
{ text: '评论审核', link: '/zh/guide/backend/moderator.md' },
|
||||
{ text: '验证码', link: '/zh/guide/backend/captcha.md' },
|
||||
{ text: '图片上传', link: '/zh/guide/backend/img-upload.md' },
|
||||
{ text: '账户与多站点', link: '/zh/guide/backend/multi-site.md' },
|
||||
{ text: '解析相对路径', link: '/zh/guide/backend/relative-path.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '进阶指南',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '表情包', link: '/zh/guide/frontend/emoticons.md' },
|
||||
{ text: '浏览量统计', link: '/zh/guide/frontend/pv.md' },
|
||||
{ text: 'LaTeX', link: '/zh/guide/frontend/latex.md' },
|
||||
{ text: '图片灯箱', link: '/zh/guide/frontend/lightbox.md' },
|
||||
{ text: '图片懒加载', link: '/zh/guide/frontend/img-lazy-load.md' },
|
||||
{ text: 'IP 属地', link: '/zh/guide/frontend/ip-region.md' },
|
||||
{ text: '多语言', link: '/zh/guide/frontend/i18n.md' },
|
||||
{ text: '开发文档', link: '/zh/develop/index.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '配置文档',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: '环境变量', link: '/zh/guide/env.md' },
|
||||
{ text: '配置文件', link: '/zh/guide/backend/config.md' },
|
||||
{ text: '界面配置', link: '/zh/guide/frontend/config.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '部署说明',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '守护进程', link: '/zh/guide/backend/daemon.md' },
|
||||
{ text: '反向代理', link: '/zh/guide/backend/reverse-proxy.md' },
|
||||
{ text: '编译构建', link: '/zh/develop/contributing.md' },
|
||||
{ text: '程序升级', link: '/zh/guide/backend/update.md' },
|
||||
{ text: 'Docker', link: '/zh/guide/backend/docker.md' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '更多内容',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: '安全防范', link: '/zh/guide/security.md' },
|
||||
{ text: '扩展阅读', link: '/zh/guide/extras.md' },
|
||||
{ text: '案例展示', link: '/zh/guide/cases.md' },
|
||||
{ text: '关于我们', link: '/zh/guide/about.md' },
|
||||
],
|
||||
},
|
||||
],
|
||||
'/zh/develop/': [
|
||||
{
|
||||
text: '开发文档',
|
||||
items: [
|
||||
{ text: '开发说明', link: '/zh/develop/index.md' },
|
||||
{ text: '贡献指南', link: '/zh/develop/contributing.md' },
|
||||
{ text: '置入博客', link: '/zh/develop/import-blog.md' },
|
||||
{ text: '置入框架', link: '/zh/develop/import-framework.md' },
|
||||
{ text: '前端 API', link: '/zh/develop/fe-api.md' },
|
||||
{ text: '前端 Event', link: '/zh/develop/event.md' },
|
||||
{ text: '插件开发', link: '/zh/develop/plugin.md' },
|
||||
{ text: '兼容性', link: '/zh/develop/compatibility.md' },
|
||||
{
|
||||
text: 'HTTP API',
|
||||
link: 'https://artalk.js.org/http-api.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
nav: [
|
||||
// NavbarItem
|
||||
{
|
||||
text: '介绍',
|
||||
link: '/zh/guide/intro',
|
||||
},
|
||||
{
|
||||
text: '部署',
|
||||
link: '/zh/guide/deploy',
|
||||
},
|
||||
{
|
||||
text: '配置',
|
||||
link: '/zh/guide/backend/config',
|
||||
},
|
||||
{
|
||||
text: '迁移',
|
||||
link: '/zh/guide/transfer',
|
||||
},
|
||||
{
|
||||
text: '案例',
|
||||
link: '/zh/guide/cases',
|
||||
},
|
||||
{
|
||||
text: '开发',
|
||||
link: '/zh/develop/',
|
||||
},
|
||||
],
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/ArtalkJS/Artalk/edit/master/docs/docs/:path',
|
||||
text: '完善文档',
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div ref="el" style="margin-top: 20px"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch, nextTick, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
import Artalk from 'artalk'
|
||||
import { ArtalkKatexPlugin } from '@artalk/plugin-katex'
|
||||
import { ArtalkLightboxPlugin } from '@artalk/plugin-lightbox'
|
||||
import 'lightgallery/css/lightgallery.css'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
const el = ref<HTMLElement | null>(null)
|
||||
|
||||
const router = useRouter()
|
||||
const page = useData().page
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initArtalk(getConfByPage())
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => router.route.path,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
artalk.update(getConfByPage())
|
||||
artalk.reload()
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
|
||||
function initArtalk(conf: any) {
|
||||
Artalk.use(ArtalkKatexPlugin)
|
||||
Artalk.use(ArtalkLightboxPlugin, {
|
||||
lightGallery: {
|
||||
lib: async () => (await import('lightgallery')).default,
|
||||
},
|
||||
})
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.on('mounted', () => {
|
||||
// TODO: Optimize the locale setting, which is overwritten by the remote server config
|
||||
const locale = document.documentElement.lang.includes('zh') ? 'zh-CN' : 'en'
|
||||
if (locale !== ctx.getConf().locale) ctx.updateConf({ locale })
|
||||
})
|
||||
})
|
||||
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
emoticons: '/assets/emoticons/default.json',
|
||||
...conf,
|
||||
})
|
||||
|
||||
loadExtraFuncs()
|
||||
}
|
||||
|
||||
function getConfByPage() {
|
||||
return {
|
||||
pageKey: `https://artalk.js.org/${router.route.path
|
||||
.replace(/^\/+/, '')
|
||||
.replace(/^zh\//, '')
|
||||
.replace(/\.html$/, '')}.html`,
|
||||
pageTitle: page.value.title,
|
||||
server: 'https://artalk.qwqaq.com',
|
||||
site: 'ArtalkDocs',
|
||||
}
|
||||
}
|
||||
|
||||
function loadExtraFuncs() {
|
||||
// 夜间模式
|
||||
const darkMode = document.querySelector('html').classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
|
||||
new MutationObserver((mList) => {
|
||||
mList.forEach((m) => {
|
||||
if (m.attributeName !== 'class') return
|
||||
|
||||
const darkMode = (<HTMLElement>m.target).classList.contains('dark')
|
||||
artalk.setDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html'), { attributes: true })
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<iframe src="https://artransfer.netlify.app/?iframe=1" style="width: 100%;height: 520px;border: 0;" id="artransferIframe"></iframe>
|
||||
<iframe
|
||||
id="artransferIframe"
|
||||
src="https://artransfer.netlify.app/?iframe=1"
|
||||
style="width: 100%; height: 520px; border: 0"
|
||||
></iframe>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -24,15 +28,10 @@ export default defineComponent({
|
||||
mList.forEach((m) => {
|
||||
if (m.attributeName !== 'class') return
|
||||
|
||||
// @ts-ignore
|
||||
const darkMode = m.target.classList.contains('dark')
|
||||
const darkMode = (<HTMLElement>m.target).classList.contains('dark')
|
||||
setArtransferIframeDarkMode(darkMode)
|
||||
})
|
||||
}).observe(document.querySelector('html'), { attributes: true })
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { inBrowser, useData } from 'vitepress'
|
||||
import { watchEffect } from 'vue'
|
||||
import './style.scss'
|
||||
|
||||
const { Layout } = DefaultTheme
|
||||
|
||||
const { lang } = useData()
|
||||
watchEffect(() => {
|
||||
if (inBrowser)
|
||||
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #doc-after>
|
||||
<Artalk />
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
File renamed without changes.
@@ -1,8 +1,8 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { Theme } from 'vitepress'
|
||||
import Layout from './Layout.vue'
|
||||
import Artalk from './Artalk.vue'
|
||||
import Artransfer from './Artransfer.vue'
|
||||
import { Theme } from 'vitepress'
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
@@ -16,5 +16,5 @@ export default {
|
||||
// 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
|
||||
@@ -8,7 +8,7 @@
|
||||
--vp-c-divider: #dfe2e5;
|
||||
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: linear-gradient(90deg,#0083ff, #37dfd9);
|
||||
--vp-home-hero-name-background: linear-gradient(90deg, #0083ff, #37dfd9);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -38,9 +38,9 @@
|
||||
background-color: #0083ff;
|
||||
|
||||
&:hover {
|
||||
border-color: #007CF0;
|
||||
border-color: #007cf0;
|
||||
color: #fff;
|
||||
background-color: #007CF0;
|
||||
background-color: #007cf0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,52 +48,52 @@
|
||||
.wave {
|
||||
display: inline-block;
|
||||
transform-origin: center bottom;
|
||||
animation:upAnimation 2.33s ease .8s 3 both;
|
||||
animation: upAnimation 2.33s ease 0.8s 3 both;
|
||||
}
|
||||
@keyframes upAnimation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
10% {
|
||||
transform: rotate(-12deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(12deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
28% {
|
||||
transform: rotate(-10deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
36% {
|
||||
transform: rotate(10deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
42% {
|
||||
transform: rotate(-8deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
48% {
|
||||
transform: rotate(8deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
52% {
|
||||
transform: rotate(-4deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
56% {
|
||||
transform: rotate(4deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
60% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.755, .5, .855, .06);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.5, 0.855, 0.06);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
transition-timing-function:cubic-bezier(0.215, .61, .355, 1);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,33 +107,38 @@ img[atk-emoticon] {
|
||||
}
|
||||
|
||||
/* 由于 body 未添加一致的 transition,导致其他地方暗黑模式切换加 transition 比较怪 */
|
||||
.vp-doc div[class*='language-'] { transition: none; }
|
||||
.vp-doc :not(pre) > code { transition: none; }
|
||||
.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
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
td,th {
|
||||
padding: .6em 1em;
|
||||
td,
|
||||
th {
|
||||
padding: 0.6em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 */
|
||||
.VPSidebar::-webkit-scrollbar {
|
||||
width: 7px
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-track {
|
||||
background-color: transparent
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-thumb {
|
||||
background-color: var(--vp-c-divider)
|
||||
background-color: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
// details block patch
|
||||
@@ -146,5 +151,5 @@ img[atk-emoticon] {
|
||||
}
|
||||
|
||||
.vp-code-group .tabs::after {
|
||||
background-color: var(--vp-c-divider)
|
||||
background-color: var(--vp-c-divider);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import { defineComponent } from 'vue'
|
||||
const component: ReturnType<typeof defineComponent>
|
||||
export default component
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
## Artalk Documentation
|
||||
# Artalk Documentation
|
||||
|
||||
> The Documentation of Artalk
|
||||
[](https://app.netlify.com/sites/artalk/deploys)
|
||||
|
||||
Documentation of Artalk
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
pnpm docs:dev
|
||||
pnpm docs:build
|
||||
pnpm dev:docs
|
||||
pnpm build:docs
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
[](https://app.netlify.com/sites/artalk/deploys)
|
||||
|
||||
文档通过 [Netlify](https://www.netlify.com/) 对 master 分支进行自动部署。
|
||||
|
||||
## Licensed
|
||||
|
||||
CC BY-NC-SA 4.0
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"latest": "2.9.1"
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
<!-- CSS -->
|
||||
<link href="https://unpkg.com/artalk@2.4.4/dist/Artalk.css" rel="stylesheet">
|
||||
<link href="http://localhost:8080/dist/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="https://unpkg.com/artalk@2.4.4/dist/Artalk.js"></script>
|
||||
<script src="http://localhost:8080/dist/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments', // 绑定元素的 Selector
|
||||
pageKey: '/post/1', // 固定链接 (留空自动获取)
|
||||
pageKey: '/post/1', // 固定链接
|
||||
pageTitle: '关于引入 Artalk 这档子事', // 页面标题 (留空自动获取)
|
||||
server: 'http://localhost:8080', // 后端地址
|
||||
site: 'Artalk 的博客', // 你的站点名
|
||||
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
# Compatibility
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
The Artalk client production environment code is built using esbuild with a target of `es2015`. However, please note that the build process only handles syntax transpilation to comply with the `es2015` standard and does not address API compatibility (no polyfills are included).
|
||||
|
||||
If your project needs to support older browsers and devices, consider including global polyfills in your bundled application. We recommend using the [Polyfill.io](https://polyfill.io/) service, which dynamically returns the necessary polyfills based on the user's User-Agent. Alternatively, you can manually add polyfills using [core-js](https://github.com/zloirock/core-js) or [Babel](https://babeljs.io/).
|
||||
|
||||
Here is a list of modern features used by Artalk:
|
||||
|
||||
- [All ECMAScript 2015 (ES6) features](https://compat-table.github.io/compat-table/es6/)
|
||||
- [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
|
||||
- [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
||||
- [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
|
||||
|
||||
## Server Compatibility
|
||||
|
||||
The Artalk server program is developed in Golang and supports operating systems such as Linux, Windows, and macOS. The current Golang version for Artalk can be found in the [go.mod](https://github.com/ArtalkJS/Artalk/blob/master/go.mod#L3). For the minimum operating system requirements for Golang, refer to the [Go Wiki](https://go.dev/wiki/MinimumRequirements).
|
||||
@@ -0,0 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
if (!import.meta.env.SSR) window.location = 'https://github.com/ArtalkJS/Artalk/blob/master/CONTRIBUTING.md';
|
||||
</script>
|
||||
@@ -0,0 +1,50 @@
|
||||
# Frontend Event
|
||||
|
||||
## Basic Events
|
||||
|
||||
| Event Name | Description |
|
||||
| ------------------- | ------------------------------- |
|
||||
| `created` | After initialization |
|
||||
| `mounted` | After data loading |
|
||||
| `updated` | After data update |
|
||||
| `unmounted` | After destruction |
|
||||
| `list-fetch` | During comment list request |
|
||||
| `list-fetched` | After comment list request |
|
||||
| `list-load` | Before comment loading |
|
||||
| `list-loaded` | After comment loading |
|
||||
| `list-failed` | When comment loading fails |
|
||||
| `list-goto-first` | When comment list resets |
|
||||
| `list-reach-bottom` | When comment list reaches bottom|
|
||||
| `comment-inserted` | After comment insertion |
|
||||
| `comment-updated` | After comment update |
|
||||
| `comment-deleted` | After comment deletion |
|
||||
| `comment-rendered` | After comment node rendering |
|
||||
| `notifies-updated` | When unread messages change |
|
||||
| `list-goto` | During comment jump |
|
||||
| `page-loaded` | After page data update |
|
||||
| `editor-submit` | When editor submits |
|
||||
| `editor-submitted` | After editor submission |
|
||||
| `user-changed` | When local user data changes |
|
||||
| `sidebar-show` | When sidebar is shown |
|
||||
| `sidebar-hide` | When sidebar is hidden |
|
||||
|
||||
Event declaration code: [@ArtalkJS/Artalk - src/types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/artalk/src/types/event.ts)
|
||||
|
||||
## Adding Event Listeners
|
||||
|
||||
```js
|
||||
artalk.on('list-loaded', () => {
|
||||
alert('Comments have been loaded')
|
||||
})
|
||||
```
|
||||
|
||||
## Removing Event Listeners
|
||||
|
||||
```js
|
||||
let foo = function () {
|
||||
/* do something */
|
||||
}
|
||||
|
||||
artalk.on('list-loaded', foo)
|
||||
artalk.off('list-loaded', foo)
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
# Frontend API
|
||||
|
||||
## Install Dependencies
|
||||
|
||||
Use your preferred package manager to install Artalk:
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
# or using npm
|
||||
npm install artalk
|
||||
```
|
||||
|
||||
## Create Instance `init`
|
||||
|
||||
Calling this function creates and returns an instantiated Artalk object for subsequent operations.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
const artalk = Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '/post/1',
|
||||
pageTitle: 'About Introducing Artalk',
|
||||
server: 'http://your_domain:8080',
|
||||
site: 'Artalk Blog',
|
||||
})
|
||||
```
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Calling this function will asynchronously send requests to the backend to:
|
||||
|
||||
1. Fetch frontend configuration
|
||||
2. Fetch comment list
|
||||
|
||||
The `mounted` event is triggered after configuration and plugins are loaded.
|
||||
|
||||
The `list-loaded` event is triggered after the comment list is loaded.
|
||||
|
||||
> Note: The frontend UI configuration may be overridden. See: [UI Configuration](../guide/frontend/config.md).
|
||||
|
||||
## Release Resources `destroy`
|
||||
|
||||
Destroy the Artalk instance to release resources.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.destroy()
|
||||
```
|
||||
|
||||
Calling this function triggers the `unmounted` event. Releasing resources involves deleting DOM nodes mounted by Artalk and removing Artalk event listeners. After this function completes, the Artalk instance will no longer be usable.
|
||||
|
||||
In frameworks like Vue/React, make sure to call this function when the component is destroyed to prevent memory leaks:
|
||||
|
||||
```ts
|
||||
import { onUnmounted } from 'vue'
|
||||
|
||||
onUnmounted(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
```
|
||||
|
||||
## Update Configuration `update`
|
||||
|
||||
Modify the current Artalk configuration.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.update({
|
||||
// new configuration...
|
||||
})
|
||||
```
|
||||
|
||||
Calling this function will trigger the `updated` event.
|
||||
|
||||
Updating the configuration will not automatically refresh the comment list. You can continue to call the `artalk.reload()` function as needed.
|
||||
|
||||
Note that this function is a member method of an instantiated object, not a global function.
|
||||
|
||||
Frontend configuration reference: [Frontend Configuration](../guide/frontend/config.md)
|
||||
|
||||
## Reload `reload`
|
||||
|
||||
Reload the Artalk comment list data from the backend.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.reload()
|
||||
```
|
||||
|
||||
The `list-load` event is triggered before loading the list, and the `list-loaded` event is triggered after the loading is complete.
|
||||
|
||||
## Listen to Events `on`
|
||||
|
||||
Add Artalk event listeners.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.on('list-loaded', (comments) => {
|
||||
// 'comments' contains all comments on the current page (not just the comments fetched in this request)
|
||||
console.log('Comments have been loaded: ', comments)
|
||||
})
|
||||
```
|
||||
|
||||
Refer to: [Frontend Event](./event.md).
|
||||
|
||||
## Remove Event Listeners `off`
|
||||
|
||||
Remove Artalk event listeners.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
const handler = () => {
|
||||
alert('Comments have been loaded')
|
||||
}
|
||||
|
||||
artalk.on('list-loaded', handler)
|
||||
artalk.off('list-loaded', handler)
|
||||
```
|
||||
|
||||
## Trigger Events `trigger`
|
||||
|
||||
Trigger Artalk events.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.trigger('list-loaded', [...])
|
||||
```
|
||||
|
||||
## Load Plugins `use`
|
||||
|
||||
Load plugins using the `Artalk.use` function. Note that this function is a global function, not a method of an instantiated object. Plugins loaded through this function will be effective for all Artalk instances.
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.editor.setContent("Hello World")
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
See: [Plugin Development](./plugin.md)
|
||||
|
||||
## Dark Mode `setDarkMode`
|
||||
|
||||
Toggle dark mode, which can be called in conjunction with the blog theme, such as when a user clicks a dark mode toggle button.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
artalk.setDarkMode(true)
|
||||
```
|
||||
|
||||
You can also set the initial dark mode by passing the `darkMode` parameter when calling `Artalk.init`.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({
|
||||
// ...other configurations
|
||||
darkMode: 'auto',
|
||||
})
|
||||
```
|
||||
|
||||
## View Count Widget `loadCountWidget`
|
||||
|
||||
See: [Page View Statistics](../guide/frontend/pv.md)
|
||||
|
||||
## Get Configuration `getConf`
|
||||
|
||||
Get the current Artalk configuration.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
const conf = artalk.getConf()
|
||||
```
|
||||
|
||||
## Get Mounted Element `getEl`
|
||||
|
||||
Get the DOM element that Artalk is currently mounted to.
|
||||
|
||||
```js
|
||||
const artalk = Artalk.init({ ... })
|
||||
|
||||
const el = artalk.getEl()
|
||||
```
|
||||
@@ -0,0 +1,175 @@
|
||||
# Import to Blog
|
||||
|
||||
## General Method
|
||||
|
||||
<<< @/code/quick-start/cdn.html
|
||||
|
||||
### When to execute `Artalk.init({})`?
|
||||
|
||||
- You can include JS and CSS resources anywhere, but ensure the JS is included before executing `Artalk.init({})`.
|
||||
- Ensure `<div id="artalk"></div>` is present in the page before executing `Artalk.init({ el: '#artalk' })`.
|
||||
|
||||
## Hugo
|
||||
|
||||
Create the template file `/themes/YOUR_THEME/layouts/partials/comment/artalk.html`:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
```html
|
||||
<link href="/lib/artalk/Artalk.css" rel="stylesheet" />
|
||||
<script src="/lib/artalk/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '{{ .Permalink }}',
|
||||
pageTitle: '{{ .Title }}',
|
||||
server: '{{ $.Site.Params.artalk.server }}',
|
||||
site: '{{ $.Site.Params.artalk.site }}',
|
||||
// ...your additional configuration
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Add the following to the appropriate place in your article template `/themes/YOUR_THEME/layouts/_default/single.html`:
|
||||
|
||||
```html
|
||||
<div class="article-comments">{{- partial "comment/artalk" . -}}</div>
|
||||
```
|
||||
|
||||
Modify the Hugo configuration file:
|
||||
|
||||
::: code-group
|
||||
|
||||
```toml [config.toml]
|
||||
[params.artalk]
|
||||
server = 'https://artalk.example.org'
|
||||
site = 'Your Site Name'
|
||||
```
|
||||
|
||||
```yaml [config.yaml]
|
||||
params:
|
||||
artalk:
|
||||
server: 'https://artalk.example.org'
|
||||
site: 'Your Site Name'
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Hexo
|
||||
|
||||
Create `/themes/YOUR_THEME/layout/comment/artalk.ejs`:
|
||||
|
||||
```html
|
||||
<link href="/lib/artalk/Artalk.css" rel="stylesheet" />
|
||||
<script src="/lib/artalk/Artalk.js"></script>
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
var artalk = Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '<%= page.permalink %>',
|
||||
pageTitle: '<%= page.title %>',
|
||||
server: '<%= theme.comment.artalk.server %>',
|
||||
site: '<%= theme.comment.artalk.site %>',
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
Modify the article template file, for example `/themes/YOUR_THEME/layout/post.ejs`:
|
||||
|
||||
```html
|
||||
<div class="article-comments"><%- partial('comment/artalk') %></div>
|
||||
```
|
||||
|
||||
Edit the theme configuration `/themes/YOUR_THEME/_config.yml`:
|
||||
|
||||
```yaml
|
||||
comment:
|
||||
artalk:
|
||||
site: 'Your Site Name'
|
||||
server: 'https://artalk.example.org'
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
For NexT theme, you can install the [Artalk plugin for Hexo NexT theme](https://github.com/leirock/hexo-next-artalk)
|
||||
|
||||
:::
|
||||
|
||||
## VitePress
|
||||
|
||||
Refer to: [docs/.vitepress](https://github.com/ArtalkJS/Artalk/tree/master/docs/docs/.vitepress)
|
||||
|
||||
- Modify `config.ts` to include CSS resources
|
||||
- Create `theme/Artalk.vue` component
|
||||
- Register the component in `theme/index.ts`
|
||||
- Use the component in `theme/Layout.vue`
|
||||
|
||||
Sample code: [docs/.vitepress/theme/Artalk.vue](https://github.com/ArtalkJS/Artalk/blob/master/docs/docs/.vitepress/theme/Artalk.vue)
|
||||
|
||||
## VuePress
|
||||
|
||||
(To be supplemented)
|
||||
|
||||
::: tip
|
||||
|
||||
Refer to: [Import to Frameworks](./import-framework.md)
|
||||
|
||||
:::
|
||||
|
||||
## Typecho
|
||||
|
||||
Modify the theme article template file, for example, `post.php`:
|
||||
|
||||
```php
|
||||
<!-- CSS -->
|
||||
<link href="/lib/artalk/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="/lib/artalk/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '<?php $this->permalink() ?>',
|
||||
pageTitle: '<?php $this->title() ?>',
|
||||
server: '<Server Address>',
|
||||
site: '<?php $this->options->title() ?>',
|
||||
// ...your additional configuration
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## WordPress
|
||||
|
||||
Modify the theme article template file, for example, `single.php`:
|
||||
|
||||
```php
|
||||
<!-- CSS -->
|
||||
<link href="/lib/artalk/Artalk.css" rel="stylesheet">
|
||||
|
||||
<!-- JS -->
|
||||
<script src="/lib/artalk/Artalk.js"></script>
|
||||
|
||||
<!-- Artalk -->
|
||||
<div id="Comments"></div>
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: '<?= addslashes(get_permalink()) ?>',
|
||||
pageTitle: '<?= addslashes(get_the_title()) ?>',
|
||||
server: '<Server Address>',
|
||||
site: '<?= addslashes(get_bloginfo('name')) ?>',
|
||||
// ...your additional configuration
|
||||
})
|
||||
</script>
|
||||
```
|
||||
@@ -0,0 +1,225 @@
|
||||
# Import to Frameworks
|
||||
|
||||
## Install Artalk
|
||||
|
||||
Install Artalk using a package manager. It is recommended to use [pnpm](https://pnpm.io/zh/).
|
||||
|
||||
```bash
|
||||
pnpm add artalk
|
||||
```
|
||||
|
||||
## Vue
|
||||
|
||||
::: code-group
|
||||
|
||||
```vue [Vue 3]
|
||||
<script lang="ts" setup>
|
||||
import Artalk from 'artalk'
|
||||
import { onMounted, onBeforeUnmount, ref } from 'vue'
|
||||
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
const el = ref<HTMLElement>()
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: `${document.title}`,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="el"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
```vue [Vue 3 + Vue Router]
|
||||
<script lang="ts" setup>
|
||||
import Artalk from 'artalk'
|
||||
import { onMounted, onBeforeUnmount, ref, watch, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
const el = ref<HTMLElement>()
|
||||
const router = useRouter()
|
||||
|
||||
let artalk: Artalk
|
||||
|
||||
onMounted(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el.value,
|
||||
pageKey: router.currentRoute.value.path,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(path) => {
|
||||
nextTick(() => {
|
||||
artalk.update({
|
||||
pageKey: path,
|
||||
pageTitle: document.title,
|
||||
})
|
||||
artalk.reload()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="el"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## React
|
||||
|
||||
::: code-group
|
||||
|
||||
```tsx [React Hooks]
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
import Artalk from 'artalk'
|
||||
|
||||
const ArtalkComment = () => {
|
||||
const container = useRef<HTMLDivElement>(null)
|
||||
const location = useLocation()
|
||||
const artalk = useRef<Artalk>()
|
||||
|
||||
useEffect(() => {
|
||||
artalk.current = Artalk.init({
|
||||
el: container.current!,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
|
||||
return () => {
|
||||
artalk.current?.destroy()
|
||||
}
|
||||
}, [location.pathname])
|
||||
|
||||
return <div ref={container}></div>
|
||||
}
|
||||
|
||||
export default ArtalkComment
|
||||
```
|
||||
|
||||
```jsx [React Class]
|
||||
import React, { createRef } from 'react'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
import Artalk from 'artalk'
|
||||
|
||||
export default class ArtalkComponent extends React.Component {
|
||||
el = createRef()
|
||||
artalk = null
|
||||
|
||||
componentDidMount() {
|
||||
this.artalk = Artalk.init({
|
||||
el: this.el.current,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.artalk?.destroy()
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div ref={this.el} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## SolidJS
|
||||
|
||||
```tsx
|
||||
import { onCleanup, onMount } from 'solid-js'
|
||||
import Artalk from 'artalk'
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
const ArtalkComment = () => {
|
||||
let el: HTMLDivElement
|
||||
let artalk: Artalk
|
||||
|
||||
onMount(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
artalk.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
return <div ref={el} />
|
||||
}
|
||||
```
|
||||
|
||||
## Svelte
|
||||
|
||||
```html
|
||||
<script>
|
||||
import Artalk from 'artalk'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
|
||||
import 'artalk/dist/Artalk.css'
|
||||
|
||||
let el
|
||||
let artalk
|
||||
|
||||
onMount(() => {
|
||||
artalk = Artalk.init({
|
||||
el: el,
|
||||
pageKey: location.pathname,
|
||||
pageTitle: document.title,
|
||||
server: 'http://localhost:8080',
|
||||
site: 'Artalk Blog',
|
||||
// ...
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (artalk) {
|
||||
artalk.destroy()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this="{el}"></div>
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
# Development Instructions
|
||||
|
||||
Welcome to the official Artalk development documentation, where you will find guidelines for building Artalk plugins and themes. Should you need to set up the Artalk development environment, please refer to the [Developer Contribution Guide](./contributing.md).
|
||||
|
||||
Thank you for your interest and support of Artalk. We deeply understand that the success of an open-source project relies on the contributions of every developer. Whether it is code, documentation, testing, or feedback, your participation is the driving force behind the project's continuous advancement. We sincerely invite you to join us in perfecting and enhancing Artalk.
|
||||
|
||||
## Index
|
||||
|
||||
- [Developer Contribution Guide](./contributing.md) ⭐️
|
||||
|
||||
**Frontend Interface**
|
||||
|
||||
- [Embed in Blog](./import-blog.md)
|
||||
- [Embed in Framework](./import-framework.md)
|
||||
- [Frontend API Documentation](./fe-api.md)
|
||||
- [Frontend Events](./event.md)
|
||||
- [Frontend Configuration](../guide/frontend/config.md)
|
||||
|
||||
**Plugin Development**
|
||||
|
||||
- [Plugin Development](./plugin.md)
|
||||
- [Plugin Development Vite Integration](https://github.com/ArtalkJS/Artalk/blob/master/ui/plugin-kit/README.md)
|
||||
|
||||
**Backend Program**
|
||||
|
||||
- <a href="/http-api.html" target="_blank">HTTP API Documentation</a>
|
||||
- [Environment Variables](../guide/env.md)
|
||||
- [Configuration Files](../guide/backend/config.md)
|
||||
|
||||
**Other Content**
|
||||
|
||||
- [Compatibility](../develop/compatibility.md)
|
||||
- [Changelog](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md)
|
||||
- [i18n Multi-language Contribution Guide](../guide/frontend/i18n.md#贡献翻译)
|
||||
- [Contributor Covenant Code of Conduct](https://github.com/ArtalkJS/Artalk/blob/master/CODE_OF_CONDUCT.md)
|
||||
|
||||
::: tip
|
||||
Artalk is under active maintenance. Please be mindful of API version compatibility. Upgrades may include breaking changes, refer to the [Changelog](https://github.com/ArtalkJS/Artalk/blob/master/CHANGELOG.md).
|
||||
:::
|
||||
@@ -0,0 +1,130 @@
|
||||
# Plugin Development
|
||||
|
||||
## Developing with Vite
|
||||
|
||||
We provide an integrated Vite plugin, enabling you to effortlessly build Artalk plugins with out-of-the-box Vite configuration.
|
||||
|
||||
Reference Documentation: [@artalk/plugin-kit](https://github.com/ArtalkJS/Artalk/blob/master/ui/plugin-kit/README.md).
|
||||
|
||||
Additionally, we offer a template repository that you can fork to develop your plugins: [artalk-plugin-sample](https://github.com/ArtalkJS/artalk-plugin-sample).
|
||||
|
||||
Utilizing Vite in combination with the frontend ecosystem, you can develop Artalk plugins using any framework you prefer, such as Vue, React, Svelte, or SolidJS.
|
||||
|
||||
## `Artalk.use`
|
||||
|
||||
Extend Artalk's functionality using `Artalk.use`.
|
||||
|
||||
```js
|
||||
import Artalk from 'artalk'
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
ctx.on('list-loaded', () => {
|
||||
console.log('Comment list loaded')
|
||||
ctx.getCommentNodes().forEach((node) => {
|
||||
node.getEl().style.background = 'red'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Artalk.use((ctx) => {
|
||||
let el = null
|
||||
|
||||
ctx.on('mounted', () => {
|
||||
el = document.createElement('div')
|
||||
document.body.appendChild(el)
|
||||
console.log('Artalk instance mounted')
|
||||
})
|
||||
|
||||
ctx.on('unmounted', () => {
|
||||
el.remove()
|
||||
console.log('Artalk instance unmounted')
|
||||
})
|
||||
})
|
||||
|
||||
const artalk = Artalk.init({ ... })
|
||||
```
|
||||
|
||||
Please note:
|
||||
|
||||
- `Artalk.use` must be called before `Artalk.init` to take effect.
|
||||
- Do not rely on the order of plugin loading; instead, listen for events to execute plugin logic.
|
||||
- The `created` event is triggered once all plugins are loaded.
|
||||
- The `mounted` event is triggered when the instance and configuration are fully loaded.
|
||||
- The `unmounted` event is triggered when the Artalk instance is destroyed.
|
||||
- The `updated` event is triggered when the configuration changes.
|
||||
|
||||
Within the use function, you can access Artalk's Context object via `ctx`.
|
||||
|
||||
### Configurable Plugin Options
|
||||
|
||||
In TypeScript, you can define a plugin with options using `ArtalkPlugin<T>`.
|
||||
|
||||
```ts
|
||||
export interface DemoPluginOptions {
|
||||
foo?: string
|
||||
}
|
||||
|
||||
export const ArtalkDemoPlugin: ArtalkPlugin<DemoPluginOptions> = (ctx, options = {}) => {
|
||||
console.log(options.foo)
|
||||
}
|
||||
```
|
||||
|
||||
Pass options when calling `Artalk.use`:
|
||||
|
||||
```ts
|
||||
import { ArtalkDemoPlugin } from 'artalk-plugin-demo'
|
||||
|
||||
Artalk.use(ArtalkDemoPlugin, { foo: 'bar' })
|
||||
```
|
||||
|
||||
## Context API
|
||||
|
||||
The Context object contains Artalk's contextual information.
|
||||
|
||||
| Member | Description |
|
||||
| -------------------- | ----------------------- |
|
||||
| `ctx.getEl` | Get container element |
|
||||
| `ctx.getConf` | Get configuration |
|
||||
| `ctx.updateConf` | Update configuration |
|
||||
| `ctx.watchConf` | Watch configuration |
|
||||
| `ctx.setDarkMode` | Set dark mode |
|
||||
| `ctx.getApi` | Get API client object |
|
||||
| -- | -- |
|
||||
| `ctx.fetch` | Fetch comment data |
|
||||
| `ctx.reload` | Reload comment list |
|
||||
| `ctx.getComments` | Get all comment data |
|
||||
| `ctx.getCommentNodes`| Get all comment nodes |
|
||||
| -- | -- |
|
||||
| `ctx.on` | Add event listener |
|
||||
| `ctx.off` | Remove event listener |
|
||||
| `ctx.trigger` | Trigger event |
|
||||
| -- | -- |
|
||||
| `ctx.get` | Get dependency |
|
||||
| `ctx.inject` | Inject dependency |
|
||||
|
||||
::: warning
|
||||
The Context API is currently unstable and subject to change. Please refer to the CHANGELOG when upgrading.
|
||||
:::
|
||||
|
||||
Reference: [@artalk/src/types/context.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/artalk/src/types/context.ts)
|
||||
|
||||
Artalk includes comprehensive TypeScript type definitions, allowing you to explore the API through editor autocompletion.
|
||||
|
||||
## Sample Plugins
|
||||
|
||||
Below is a list of externally maintained Artalk plugins:
|
||||
|
||||
| Plugin | Description |
|
||||
| -------------------------------------------------------------------------------------------- | ----------------------- |
|
||||
| [artalk-plugin-sample](https://github.com/ArtalkJS/artalk-plugin-sample) | Sample Plugin |
|
||||
| [@artalk/plugin-katex](https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-katex) | LaTeX Formula Plugin |
|
||||
| [@artalk/plugin-auth](https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-auth) | Auth Plugin (SolidJS) |
|
||||
| [@artalk/plugin-lightbox](https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-lightbox) | Basic Image Lightbox |
|
||||
|
||||
Additionally, many plugins are implemented within Artalk itself, which you can reference for developing your own plugins:
|
||||
|
||||
[@ArtalkJS/Artalk - src/plugins](https://github.com/ArtalkJS/Artalk/tree/master/ui/artalk/src/plugins)
|
||||
|
||||
## Backend Plugin Development
|
||||
|
||||
【TODO】
|
||||
@@ -0,0 +1,3 @@
|
||||
# About Us
|
||||
|
||||
【TODO】
|
||||
@@ -0,0 +1,403 @@
|
||||
# Multi-channel Notifications
|
||||
|
||||
Artalk supports sending administrator notifications through its multi-channel notification feature in various ways.
|
||||
|
||||
Supported platforms include **Telegram**, **Feishu**, **DingTalk**, **Bark**, **Slack**, and **LINE**, with the ability to enable multiple methods simultaneously.
|
||||
|
||||
You can modify these configurations in the settings interface of the [Dashboard](../frontend/sidebar.md#Settings) or via the [configuration file](../backend/config.md#multi-channel-notifications-admin-notify) or [environment variables](../env.md#multi-channel-notifications).
|
||||
|
||||
## Configuration File
|
||||
|
||||
The complete `admin_notify` configuration is as follows:
|
||||
|
||||
::: details Click to reveal
|
||||
|
||||
```yaml
|
||||
# Multi-channel Notifications
|
||||
admin_notify:
|
||||
# Notification Template
|
||||
notify_tpl: default
|
||||
noise_mode: false
|
||||
# Email notification for administrators
|
||||
email:
|
||||
enabled: true # Disable email notifications for administrators when using other push methods
|
||||
mail_subject: '[{{site_name}}] Your article "{{page_title}}" has a new reply'
|
||||
mail_tpl: ''
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: false
|
||||
api_token: ''
|
||||
receivers:
|
||||
- 7777777
|
||||
# Feishu
|
||||
lark:
|
||||
enabled: false
|
||||
webhook_url: ''
|
||||
# DingTalk
|
||||
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: ''
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Email Notifications
|
||||
|
||||
Send message notifications to administrators via email.
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
enabled: true # Disable email notifications for administrators when using other push methods
|
||||
mail_subject: '[{{site_name}}] Your article "{{page_title}}" has a new reply'
|
||||
mail_tpl: ''
|
||||
```
|
||||
|
||||
You can disable email notifications when using other push methods.
|
||||
|
||||
Before doing this, you need to configure the global email sending functionality: [refer here](./email.md).
|
||||
|
||||
### Administrator Email Template
|
||||
|
||||
- The `mail_subject` configuration item is the email subject sent to the administrator.
|
||||
- The `mail_tpl` configuration item specifies the email template for the administrator (fill in the email template file path).
|
||||
|
||||
(If this item is empty, it will inherit the `email.mail_tpl` configuration item).
|
||||
|
||||
## Telegram
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# Telegram
|
||||
telegram:
|
||||
enabled: true
|
||||
api_token: ''
|
||||
receivers:
|
||||
- 7777777
|
||||
```
|
||||
|
||||
- `api_token`: The API token for the TG Bot.
|
||||
- `receivers`: The numeric IDs of the message recipients, multiple recipients can be set.
|
||||
|
||||
### Creating a TG Bot
|
||||
|
||||
Search for `@BotFather`, reply with `/newbot`, and follow the prompts to create a new TG bot.
|
||||
|
||||

|
||||
|
||||
The red text is the `api_token` you need to fill in Artalk's configuration.
|
||||
|
||||
Fill in the `receivers` field with the numeric IDs of the accounts that need to receive messages. You can use `@RawDataBot` to get these IDs, as shown:
|
||||
|
||||

|
||||
|
||||
For more details, refer to: [Bots: An introduction for developers - Telegram](https://core.telegram.org/bots)
|
||||
|
||||
::: tip
|
||||
|
||||
Given the complex network environment, if you need to use a proxy, configure the environment variable before starting Artalk, for example:
|
||||
|
||||
```sh
|
||||
export https_proxy=http://127.0.0.1:7890
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Feishu
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# Feishu
|
||||
lark:
|
||||
enabled: true
|
||||
webhook_url: ''
|
||||
```
|
||||
|
||||
- `webhook_url`: The WebHook address obtained when creating the group bot.
|
||||
|
||||
### Creating a Group Bot
|
||||
|
||||
Click the plus sign at the top to create a new group:
|
||||
|
||||

|
||||
|
||||
Find "Group Settings" on the right, select "Group Bot", click "Add Bot", choose "Custom Bot", and follow the prompts to create it.
|
||||
|
||||
<img src="/images/notify/lark-2.png" width="700px">
|
||||
|
||||
Copy the WebHook address as shown above and modify Artalk's `webhook_url` configuration.
|
||||
|
||||
<img src="/images/notify/lark-3.png" width="400px">
|
||||
|
||||
For more details, refer to: [Feishu Help Center Documentation](https://www.feishu.cn/hc/zh-CN/articles/360024984973)
|
||||
|
||||
## DingTalk
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
# DingTalk
|
||||
ding_talk:
|
||||
enabled: true
|
||||
token: ''
|
||||
secret: ''
|
||||
```
|
||||
|
||||
Refer to: [DingTalk Open Documentation](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) is an open-source iOS app that [supports self-hosting](https://github.com/Finb/bark-server), allowing you to easily push messages to your iOS device.
|
||||
|
||||
You can search and download it from the App Store and get the `server` configuration item to fill in Artalk:
|
||||
|
||||
<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
|
||||
```
|
||||
|
||||
## Notification Template
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
notify_tpl: default
|
||||
```
|
||||
|
||||
The `admin_notify.notify_tpl` configuration item can be set to a custom notification template file path. The default notification template is:
|
||||
|
||||
```
|
||||
@{{reply_nick}}:
|
||||
|
||||
{{reply_content}}
|
||||
|
||||
{{link_to_reply}}
|
||||
```
|
||||
|
||||
The available variables are the same as in the email template, refer to: [Email Template](./email.md#邮件模板)
|
||||
|
||||
## Notify Pending Comments `notify_pending`
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
notify_pending: false
|
||||
```
|
||||
|
||||
`notify_pending` is turned off by default. When set to `false`, pending comments will not send notifications. You can view all pending comments in the Dashboard.
|
||||
|
||||
## Noise Mode `noise_mode`
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
noise_mode: false
|
||||
```
|
||||
|
||||
`noise_mode` is turned off by default. When set to `false`, only messages directed to the administrator will send notifications. For example, communication between "User A" and "User B" will not notify the administrator.
|
||||
|
||||
Note: When `moderator.pending_default` is set to `true`, noise_mode is always enabled.
|
||||
|
||||
## WebHook Callback
|
||||
|
||||
When WebHook is enabled, a new comment will send a **POST** request with `application/json` type Body data to the specified WebHook address.
|
||||
|
||||
You can write your own server-side code to handle requests from Artalk.
|
||||
|
||||
**Artalk Configuration File**
|
||||
|
||||
```yaml
|
||||
admin_notify:
|
||||
webhook:
|
||||
enabled: true
|
||||
url: http://localhost:8080/
|
||||
```
|
||||
|
||||
**Body Data Content**
|
||||
|
||||
`application/json` type
|
||||
|
||||
| Key | Description | Type | Remarks |
|
||||
| ---------------- | ----------------- | ------ | ----------------------------------------- |
|
||||
| `notify_subject` | Notification Title | String | Corresponds to the admin_notify.notify_subject configuration item |
|
||||
| `notify_body` | Notification Content | String | Rendered according to the admin_notify.notify_tpl template |
|
||||
| `comment` | Comment Content | Object | New comment data object |
|
||||
| `parent_comment` | Target of Comment Reply | Object | If it's a root comment, the value is null |
|
||||
|
||||
**Body Data Sample**
|
||||
|
||||
```js
|
||||
{
|
||||
"notify_subject": "",
|
||||
"notify_body": "@TestUser:\n\nTestContent\n\nhttps://127.0.0.1/index.html?atk_comment=1057",
|
||||
"comment": {
|
||||
"id": 1057,
|
||||
"content": "TestContent",
|
||||
"user_id": 226,
|
||||
"nick": "TestUser",
|
||||
"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 Handling Example**
|
||||
|
||||
```js
|
||||
const express = require('express')
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(express.json()) // Use JSON middleware
|
||||
|
||||
app.post('/', (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 Handling Example**
|
||||
|
||||
```js
|
||||
const http = require('http')
|
||||
|
||||
const requestListener = (req, res) => {
|
||||
// receive json request
|
||||
let body = ''
|
||||
req.on('data', (data) => {
|
||||
body += data
|
||||
})
|
||||
req.on('end', () => {
|
||||
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 Handling Example**
|
||||
|
||||
```php
|
||||
Route::get('/', function (Request $request) {
|
||||
$data = $request->json()->all();
|
||||
$notify_subject = $data["notify_subject"];
|
||||
$notify_body = $data["notify_body"];
|
||||
});
|
||||
```
|
||||
|
||||
**Golang net/http Handling Example**
|
||||
|
||||
```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,159 @@
|
||||
# Captcha
|
||||
|
||||
Artalk comes with a built-in image captcha feature, allowing you to configure operation frequency limits that activate the captcha when exceeded.
|
||||
|
||||
Additionally, you can integrate various verification service providers to gain diversified verification functionality.
|
||||
|
||||
Currently, Artalk supports: Turnstile, reCAPTCHA, hCaptcha, and Geetest.
|
||||
|
||||
You can modify these settings in the [Dashboard](../frontend/sidebar.md#Settings) or via the [configuration file](./config.md#captcha) or [environment variables](../env.md#captcha).
|
||||
|
||||
## Configuration File
|
||||
|
||||
The full `captcha` configuration is as follows:
|
||||
|
||||
```yaml
|
||||
# Captcha
|
||||
captcha:
|
||||
enabled: true # Master switch
|
||||
always: false # Always require captcha
|
||||
captcha_type: image # Type of captcha
|
||||
action_limit: 3 # Number of actions required to activate captcha
|
||||
action_reset: 60 # Timeout to reset action counter (unit: s, set to -1 to never reset)
|
||||
# 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**: When set to `true`, captcha is always required.
|
||||
- **captcha_type**: Type of captcha, options include: `image`, `turnstile`, `recaptcha`, `hcaptcha`, `geetest`.
|
||||
- **action_limit**: The number of actions required to activate the captcha.
|
||||
- **action_reset**: When the time exceeds this value, the action counter resets. Unit is seconds; set to `-1` to never reset.
|
||||
|
||||
Note: When `always` is enabled, `action_limit` and `action_reset` configurations are ignored.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Example 1
|
||||
|
||||
Within a 60-second time frame, if the number of actions exceeds 3, captcha will be required:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
action_limit: 3
|
||||
action_reset: 60
|
||||
```
|
||||
|
||||
The counter will automatically reset after 60 seconds, allowing for 3 more actions without requiring a captcha.
|
||||
|
||||
### Example 2
|
||||
|
||||
Regardless of the time frame, if the number of actions from an IP address exceeds 5, captcha will be required:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
action_limit: 5
|
||||
action_reset: -1
|
||||
```
|
||||
|
||||
### Example 3
|
||||
|
||||
Always require captcha, regardless of the number of actions:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
always: true
|
||||
```
|
||||
|
||||
## Definition of Actions
|
||||
|
||||
Each "comment, vote, image upload, password verification" by an IP address counts as an "action."
|
||||
|
||||
## Turnstile
|
||||
|
||||
[Turnstile](https://www.cloudflare.com/zh-cn/products/turnstile/) is a verification service from Cloudflare. You can obtain the `site_key` and `secret_key` from the CF dashboard, then fill in these keys in the Artalk settings and change `captcha_type` to `turnstile`.
|
||||
|
||||
Illustrations:
|
||||
|
||||
<img src="/images/captcha/cf-turnstile-1.png" width="400px">
|
||||
|
||||
<img src="/images/captcha/cf-turnstile-2.png" width="400px">
|
||||
|
||||
Corresponding configuration file:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# Omit other configurations...
|
||||
captcha_type: turnstile
|
||||
turnstile:
|
||||
site_key: ''
|
||||
secret_key: ''
|
||||
```
|
||||
|
||||
## reCAPTCHA
|
||||
|
||||
[reCAPTCHA](https://developers.google.com/recaptcha) is a verification service from Google, and Artalk supports reCAPTCHA v3. You can obtain the `site_key` and `secret_key` from the Google developer dashboard, then fill in these keys in the Artalk settings and change `captcha_type` to `recaptcha`.
|
||||
|
||||
Corresponding configuration file:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# Omit other configurations...
|
||||
captcha_type: recaptcha
|
||||
recaptcha:
|
||||
site_key: ''
|
||||
secret_key: ''
|
||||
```
|
||||
|
||||
Note: Accessing Google APIs from within China may require configuring a system proxy.
|
||||
|
||||
Google provides test keys for reCAPTCHA: [see here](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/) is a verification service. You can obtain the `site_key` and `secret_key` from its official website, then fill in these keys in the Artalk settings and change `captcha_type` to `hcaptcha`.
|
||||
|
||||
Corresponding configuration file:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# Omit other configurations...
|
||||
captcha_type: hcaptcha
|
||||
hcaptcha:
|
||||
site_key: ''
|
||||
secret_key: ''
|
||||
```
|
||||
|
||||
hCaptcha provides test keys: [see here](https://docs.hcaptcha.com/#integration-testing-test-keys).
|
||||
|
||||
## Geetest
|
||||
|
||||
Artalk supports integrating [Geetest](https://www.geetest.com/adaptive-captcha) for advanced behavior verification.
|
||||
|
||||
First, register an account on the Geetest website to obtain the `captcha_id` and `captcha_key`, then modify the configuration in the Artalk settings and change `captcha_type` to `geetest`.
|
||||
|
||||
Corresponding configuration file:
|
||||
|
||||
```yaml
|
||||
captcha:
|
||||
# Omit other configurations...
|
||||
captcha_type: geetest
|
||||
geetest:
|
||||
captcha_id: ''
|
||||
captcha_key: ''
|
||||
```
|
||||
@@ -0,0 +1,360 @@
|
||||
# Configuration File
|
||||
|
||||
## Easy Configuration
|
||||
|
||||
It is recommended to modify the configuration through the graphical interface in the sidebar "[Dashboard](../frontend/sidebar.md)" without manually editing the configuration file.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can configure via environment variables, refer to the documentation: [Environment Variables](../env.md).
|
||||
|
||||
## Specifying the Configuration File Path
|
||||
|
||||
By default, Artalk uses `artalk.yml` in the working directory as the configuration file. You can specify a specific file using the `-c` parameter:
|
||||
|
||||
```bash
|
||||
artalk -c ./conf.yml
|
||||
```
|
||||
|
||||
## Obtaining a Template Configuration File
|
||||
|
||||
You can refer to a "complete configuration file": [artalk.example.zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/conf/artalk.example.zh-CN.yml)
|
||||
|
||||
### Generate Configuration File Using the `gen` Command
|
||||
|
||||
Artalk provides the `gen` command, allowing you to quickly generate a new configuration file:
|
||||
|
||||
```bash
|
||||
artalk gen conf -lang zh-CN ./artalk.yml
|
||||
```
|
||||
|
||||
### Download Configuration File via Command Line
|
||||
|
||||
```bash
|
||||
wget -O artalk.yml https://raw.githubusercontent.com/ArtalkJS/Artalk/master/conf/artalk.example.zh-CN.yml
|
||||
```
|
||||
|
||||
## Encryption Key `app_key`
|
||||
|
||||
Before starting Artalk, you need to configure an `app_key` for securely encrypting site content:
|
||||
|
||||
```yaml
|
||||
app_key: <any random characters>
|
||||
```
|
||||
|
||||
## Language `locale`
|
||||
|
||||
Set the language for Artalk. Follows the Unicode BCP 47 standard, with the default being "zh-CN" (Simplified Chinese).
|
||||
|
||||
```yaml
|
||||
locale: zh-CN
|
||||
```
|
||||
|
||||
For details, refer to: [Multilingual](../frontend/i18n.md)
|
||||
|
||||
## Database `db`
|
||||
|
||||
Artalk supports connecting to various databases, including SQLite, MySQL, PostgreSQL, and SQL Server. The configurations are as follows:
|
||||
|
||||
#### SQLite
|
||||
|
||||
SQLite is a lightweight database that stores data in a single file, requiring no additional running programs. It is especially suitable for small sites, such as personal blogs.
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: sqlite
|
||||
file: ./data/artalk.db
|
||||
```
|
||||
|
||||
#### MySQL / PostgreSQL / SQL Server
|
||||
|
||||
Modify `type` to your database type:
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: mysql # sqlite, mysql, pgsql, mssql
|
||||
name: artalk # Database name
|
||||
host: localhost # Address
|
||||
port: 3306 # Port
|
||||
user: root # Username
|
||||
password: '' # Password
|
||||
charset: utf8mb4 # Charset
|
||||
table_prefix: '' # Table prefix (e.g., "atk_")
|
||||
ssl: false # Enable SSL
|
||||
prepare_stmt: true # Prepared statements
|
||||
```
|
||||
|
||||
The data tables will be automatically created when Artalk starts, no additional operation is required.
|
||||
|
||||
#### Database Connection String (DSN)
|
||||
|
||||
If needed, you can manually configure `db.dsn` to specify the database connection string, for example:
|
||||
|
||||
```yaml
|
||||
db:
|
||||
type: mysql
|
||||
dsn: mysql://myuser:mypassword@localhost:3306/mydatabase?tls=skip-verify
|
||||
```
|
||||
|
||||
For more details, refer to: [@go-sql-driver/mysql:README.md](https://github.com/go-sql-driver/mysql)
|
||||
|
||||
## Server `http`
|
||||
|
||||
```yaml
|
||||
http:
|
||||
# Request body size limit (unit: MB)
|
||||
body_limit: 100
|
||||
# Proxy header name (when using CDN, fill in `X-Forwarded-For` to get the user's real IP)
|
||||
proxy_header: ""
|
||||
```
|
||||
|
||||
## Admin Users `admin_users`
|
||||
|
||||
You need to configure administrator accounts to manage site content through the "[Dashboard](../frontend/sidebar.md)".
|
||||
|
||||
Artalk supports multiple sites, allowing you to create multiple administrator accounts and assign sites to them, enabling your friends to share the same backend program.
|
||||
|
||||
For details, refer to: [Admin Users × Multi-site](./multi-site.md)
|
||||
|
||||
## Trusted Domains `trusted_domains`
|
||||
|
||||
```yaml
|
||||
trusted_domains:
|
||||
- https://frontend.domainA.com
|
||||
- https://frontend.domainB.com
|
||||
```
|
||||
|
||||
Only the domains in the list are allowed to access the backend API, restricting cross-domain requests from external domains.
|
||||
|
||||
::: tip
|
||||
|
||||
You need to add the URL address of the "frontend page" to the trusted domains list.
|
||||
|
||||
If it's not the default 80/443 port, include the port number, e.g., `https://example.com:8080`.
|
||||
|
||||
:::
|
||||
|
||||
In the sidebar [Dashboard](../frontend/sidebar.md#控制中心), under the "Site" tab - select the site and "Modify URL", fill in the site URL to achieve the same setting effect; to add multiple URLs, separate them with commas `,`. After modification, manually restart Artalk.
|
||||
|
||||
The default site address `ATK_SITE_URL` configuration item will also be automatically added to the trusted domains list.
|
||||
|
||||
Additionally, you can configure trusted domains via the environment variable `ATK_TRUSTED_DOMAINS` when starting, for example:
|
||||
|
||||
```bash
|
||||
ATK_TRUSTED_DOMAINS="https://a.com https://b.org" artalk server
|
||||
```
|
||||
|
||||
Note that environment variables will override the settings in the configuration file.
|
||||
|
||||
Technical details: `trusted_domains` configuration controls cross-origin requests by setting the HTTP response header `Access-Control-Allow-Origin` (refer to: [W3C Cross-Origin Resource Sharing](https://fetch.spec.whatwg.org/#http-cors-protocol) / [OWASP Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)).
|
||||
|
||||
## Default Site `site_default`
|
||||
|
||||
If you think you won't use the multi-site feature of Artalk, you can directly set this item to your site name, for example:
|
||||
|
||||
```yaml
|
||||
site_default: Artalk Official Website
|
||||
```
|
||||
|
||||
Then use this site name in the frontend:
|
||||
|
||||
```js
|
||||
Artalk.init({ site: 'Artalk Official Website' })
|
||||
```
|
||||
|
||||
This way, you don't need to manually create the site in the sidebar [Dashboard](../frontend/sidebar.md#控制中心).
|
||||
|
||||
## Frontend Configuration `frontend`
|
||||
|
||||
In Artalk's configuration file `artalk.yml`, you can configure the `frontend` field to control the frontend interface, for example:
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
placeholder: Type content...
|
||||
noComment: "Silence is golden"
|
||||
sendBtn: Send Comment
|
||||
emoticons: 'https://raw.githubusercontent.com/ArtalkJS/Emoticons/master/grps/default.json'
|
||||
# ----- Omitted -------
|
||||
# Keep the same as the frontend configuration item name
|
||||
```
|
||||
|
||||
A complete backend `frontend` field configuration file can be referred to: [artalk.example.zh-CN.yml](https://github.com/ArtalkJS/Artalk/blob/master/conf/artalk.example.zh-CN.yml)
|
||||
|
||||
## Email Notifications `email`
|
||||
|
||||
Configure email notifications to notify the target user of replies via email. You can customize the sender name, title, template, etc.
|
||||
|
||||
For details, refer to: [Backend · Email Notifications](./email.md)
|
||||
|
||||
## Multi-channel Notifications `admin_notify`
|
||||
|
||||
You can configure various message sending methods, such as Feishu, Telegram, etc., to notify administrators when new comments are received.
|
||||
|
||||
For details, refer to: [Backend · Multi-channel Notifications](./admin_notify.md)
|
||||
|
||||
## Comment Moderation `moderator`
|
||||
|
||||
Configure comment moderation to automatically intercept spam comments.
|
||||
|
||||
For details, refer to: [Backend · Comment Moderation](./moderator.md)
|
||||
|
||||
## Captcha `captcha`
|
||||
|
||||
Supports image and slide captchas to limit request frequency.
|
||||
|
||||
For details, refer to: [Backend · Captcha](./captcha.md)
|
||||
|
||||
## Cache `cache`
|
||||
|
||||
To save memory resources, caching is disabled by default. If you have high performance requirements for your site, enable it manually. You can also connect to external cache servers, supporting Redis and Memcache.
|
||||
|
||||
```yaml
|
||||
cache:
|
||||
enabled: true # Enable cache (default is off)
|
||||
type: builtin # Supports redis, memcache, builtin (built-in cache)
|
||||
expires: 30 # Cache expiration time (unit: minutes)
|
||||
warm_up: false # Warm up cache on program startup
|
||||
server: '' # Connect to cache server (e.g., "localhost:6379")
|
||||
```
|
||||
|
||||
- **warm_up**: Cache warm-up feature. Set to `true`, it will immediately cache all database content when Artalk starts. If you have a large number of comments, this may extend the startup time.
|
||||
- **type**: Cache type, defaults to `builtin`. Options: `redis`, `memcache`, `builtin`.
|
||||
|
||||
Note: If you modify the database content outside the Artalk program, you need to refresh the Artalk cache to update it.
|
||||
|
||||
---
|
||||
|
||||
Redis authentication and database configuration:
|
||||
|
||||
```yaml
|
||||
# Cache
|
||||
cache:
|
||||
# Omit other configurations...
|
||||
redis:
|
||||
network: tcp # Connection method (tcp or unix)
|
||||
username: '' # Username
|
||||
password: '' # Password
|
||||
db: 0 # Use database 0
|
||||
```
|
||||
|
||||
<!-- Technical details: [Artalk Cache Mechanism Sequence Diagram.png](/images/artalk/artalk-cache.png) -->
|
||||
<!--  -->
|
||||
|
||||
## Listening Address `host`
|
||||
|
||||
The default HTTP port for Artalk is 23366. You can specify it in the configuration file:
|
||||
|
||||
```yaml
|
||||
host: '0.0.0.0'
|
||||
port: 23366
|
||||
```
|
||||
|
||||
Configuring the `host` listening address to `0.0.0.0` will expose the Artalk service to the entire network.
|
||||
|
||||
If you want Artalk to be accessible only locally, configure `host` to `127.0.0.1`.
|
||||
|
||||
When starting Artalk via the command line, you can specify the address and port using the `--host` and `--port` parameters, for example:
|
||||
|
||||
```bash
|
||||
artalk server --host 127.0.0.1 --port 8080
|
||||
```
|
||||
|
||||
## Encrypted Transmission `ssl`
|
||||
|
||||
```yaml
|
||||
ssl:
|
||||
enabled: true
|
||||
cert_path: ''
|
||||
key_path: ''
|
||||
```
|
||||
|
||||
You can configure this item to upgrade HTTP to HTTPS, encrypting data transmission via SSL.
|
||||
|
||||
- `cert_path`: Path to the SSL certificate public key file.
|
||||
- `key_path`: Path to the SSL certificate private key file.
|
||||
|
||||
You can also directly reverse proxy the Artalk local server and then enable HTTPS in, for example, Nginx.
|
||||
|
||||
## Time Zone Configuration `timezone`
|
||||
|
||||
```yaml
|
||||
timezone: Asia/Shanghai
|
||||
```
|
||||
|
||||
Fill in the time zone where you are located, corresponding to the IANA database time zone name, refer to: [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 `login_timeout`
|
||||
|
||||
This value sets the validity period of the administrator account login JWT token, in seconds.
|
||||
|
||||
For example, valid for 3 days:
|
||||
|
||||
```yaml
|
||||
login_timeout: 259200
|
||||
```
|
||||
|
||||
## Log Configuration `log`
|
||||
|
||||
When logging is enabled, system errors and other information will be recorded in the specified file.
|
||||
|
||||
```yaml
|
||||
log:
|
||||
enabled: true # Master switch
|
||||
filename: ./data/artalk.log # Log file path
|
||||
```
|
||||
|
||||
Logs are in JSON format, which can be viewed using the `tail` command combined with the `jq` tool in Linux:
|
||||
|
||||
```bash
|
||||
tail -f ./data/artalk.log | jq
|
||||
```
|
||||
|
||||
If you are a Grafana user, you can use Loki and Promtail to collect and query logs.
|
||||
|
||||
## Debug Mode `debug`
|
||||
|
||||
Set `debug` to `true` to enable debug mode.
|
||||
|
||||
```yaml
|
||||
debug: true
|
||||
```
|
||||
|
||||
## Working Directory `-w` Parameter
|
||||
|
||||
If the working directory is not specified, Artalk will use the "directory where the program is started" as the working directory.
|
||||
|
||||
```bash
|
||||
pwd # Display the current directory path
|
||||
```
|
||||
|
||||
Use the `-w` parameter to specify the working directory, which is usually an "absolute path", for example:
|
||||
|
||||
```bash
|
||||
artalk -w /root/artalk -c ./conf.yml
|
||||
```
|
||||
|
||||
Note: The relative path of `-c` will be based on the path of `-w`, and Artalk will read `/root/artalk/conf.yml` as the configuration file.
|
||||
|
||||
Additionally, the "relative path" used in the "configuration file" will also be based on the "working directory".
|
||||
|
||||
For example, if the configuration in `conf.yml` is:
|
||||
|
||||
```yaml
|
||||
test_file: ./data/artalk.log
|
||||
```
|
||||
|
||||
It will read `/root/artalk/data/artalk.log`.
|
||||
|
||||
::: tip
|
||||
|
||||
Configuration file related code: [/internal/config/config.go](https://github.com/ArtalkJS/Artalk/blob/master/internal/config/config.go)
|
||||
|
||||
Go to: [Frontend Configuration](../frontend/config.md)
|
||||
:::
|
||||
@@ -0,0 +1,144 @@
|
||||
# Daemon Process
|
||||
|
||||
## Systemd
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- The Artalk binary file, which can be downloaded from [GitHub Release](https://github.com/ArtalkJS/Artalk/releases)
|
||||
- `systemctl --version` 232 or later
|
||||
- `sudo` administrator privileges
|
||||
|
||||
Move Artalk to `$PATH`, for example:
|
||||
|
||||
```bash
|
||||
sudo mv artalk /usr/bin/
|
||||
```
|
||||
|
||||
Test if it works:
|
||||
|
||||
```bash
|
||||
artalk version
|
||||
```
|
||||
|
||||
Create a user group named `artalk`:
|
||||
|
||||
```bash
|
||||
sudo groupadd --system artalk
|
||||
```
|
||||
|
||||
Create a user named `artalk` with a writable home directory:
|
||||
|
||||
```bash
|
||||
sudo useradd --system \
|
||||
--gid artalk \
|
||||
--create-home \
|
||||
--home-dir /var/lib/artalk \
|
||||
--shell /usr/sbin/nologin \
|
||||
--comment "Artalk server" \
|
||||
artalk
|
||||
```
|
||||
|
||||
If you have the Artalk configuration file, ensure that the newly created `artalk` user has read permissions.
|
||||
|
||||
Create the service file:
|
||||
|
||||
```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=simple
|
||||
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
|
||||
```
|
||||
|
||||
Carefully check `ExecStart` and `ExecReload`. Ensure that the binary file location and program startup parameters are correct.
|
||||
|
||||
For example, change the path of the `-c` parameter to specify the configuration file and the `-w` parameter to change the working directory.
|
||||
|
||||
Note that all relative paths in the configuration file are based on the working directory. For example, the `./data/` folder in the configuration file, if the startup parameter is `-w /var/lib/artalk`, will read from the `/var/lib/artalk/data/` directory. Ensure the files in this directory are readable and writable by the created `artalk` account.
|
||||
|
||||
After saving the service file, you can set the service to start automatically:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now artalk
|
||||
```
|
||||
|
||||
Verify that the service is running correctly:
|
||||
|
||||
```bash
|
||||
systemctl status artalk
|
||||
```
|
||||
|
||||
Some common commands:
|
||||
|
||||
- Start the service: `systemctl start artalk`
|
||||
- Stop the service: `systemctl stop artalk`
|
||||
- Check the status: `systemctl status artalk`
|
||||
- View logs: `journalctl -u artalk --no-pager | less +G`
|
||||
|
||||
## Tmux
|
||||
|
||||
tmux will create a persistent command-line session that remains in the background after SSH or tty disconnection.
|
||||
|
||||
Note: tmux sessions will be cleared after the server shuts down or restarts, and you will need to manually rerun the program.
|
||||
|
||||
1. Create a session `tmux new -s artalk`
|
||||
2. Run the program `./artalk server`
|
||||
|
||||
Reconnect to the session: `tmux attach -t artalk`
|
||||
|
||||
View all sessions: `tmux ls`
|
||||
|
||||
## Supervisor
|
||||
|
||||
Taking Baota Panel as an example: Open the "Software Store," search for and install the "Supervisor Manager":
|
||||
|
||||

|
||||
|
||||
After installation, open the plugin and click "Add Daemon":
|
||||
|
||||

|
||||
|
||||
> - Startup User: `root` or others
|
||||
> - Working Directory: Click the icon on the right and select the Artalk directory
|
||||
> - Startup Command: `./artalk server`
|
||||
|
||||
## Docker
|
||||
|
||||
Update the Docker container's [Restart Policy](https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) to achieve the effect of process daemonization.
|
||||
|
||||
```bash
|
||||
docker update --restart=unless-stopped artalk
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
Add the `restart: unless-stopped` policy to the service in the `docker-compose.yml` file:
|
||||
|
||||
```diff
|
||||
version: '3'
|
||||
services:
|
||||
artalk:
|
||||
+ restart: unless-stopped
|
||||
```
|
||||
Loaded 100 of 1114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user