Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67f3daae62 | ||
|
|
c72fa68e4c | ||
|
|
1f5cda452c | ||
|
|
5a19d2513d | ||
|
|
c0f8145615 | ||
|
|
2ad14b82fa | ||
|
|
c8bdcdc778 | ||
|
|
15d2bd8484 | ||
|
|
7d4311115c | ||
|
|
3efa0582ef | ||
|
|
7624babc99 | ||
|
|
6cf8f350f5 | ||
|
|
0e08555220 | ||
|
|
fe738fa7d4 | ||
|
|
2439f4ab02 | ||
|
|
75b2b146ce | ||
|
|
1f3c096517 | ||
|
|
d264388c00 | ||
|
|
b8eaeeaa64 | ||
|
|
dba1a486cb | ||
|
|
5408ae6ad8 | ||
|
|
f5430a8925 | ||
|
|
9cc21204b1 | ||
|
|
f911e06a69 | ||
|
|
4a6eab0ce6 | ||
|
|
1d90fadd0c | ||
|
|
1b1a5ec94d | ||
|
|
d67379f297 | ||
|
|
249b98df66 | ||
|
|
aebbb5cc7d | ||
|
|
4f020aa8a5 | ||
|
|
02426c507a | ||
|
|
ffb7ae79eb | ||
|
|
f58f2f3cd6 | ||
|
|
794abc3d7a | ||
|
|
da406014ad | ||
|
|
9842a24a50 | ||
|
|
fb21ec04a6 | ||
|
|
15b74207dd | ||
|
|
6055e9b6ba | ||
|
|
df3e0814c4 | ||
|
|
81f660dc49 | ||
|
|
f56e2a07c6 | ||
|
|
a2fce6cf0b | ||
|
|
ccb5356bfa | ||
|
|
f44084dfd2 | ||
|
|
948d76f98f | ||
|
|
6606f32678 | ||
|
|
6af12d49fc | ||
|
|
9decb49d04 | ||
|
|
69466c1c41 | ||
|
|
adf5163a79 | ||
|
|
b40bc1b487 | ||
|
|
9d30ccf1f0 | ||
|
|
396810eddf | ||
|
|
5dbf6694b1 | ||
|
|
c2df8d3fa3 | ||
|
|
c149662d54 | ||
|
|
ae1c5e38d1 | ||
|
|
718b46141e | ||
|
|
9c8986be10 | ||
|
|
1d12ad73c6 | ||
|
|
30d4fc84a9 | ||
|
|
834e7750ce | ||
|
|
f38522ca0b | ||
|
|
d87821ee02 | ||
|
|
bd9e226c36 | ||
|
|
2809267d7a | ||
|
|
4de24da9d5 | ||
|
|
580981a468 | ||
|
|
5bff2c8fa5 | ||
|
|
338de1f04c | ||
|
|
5e867c86c3 | ||
|
|
07efe717d0 | ||
|
|
2b4c066a2e | ||
|
|
c03e82e4e7 | ||
|
|
a91d7c000a | ||
|
|
faf9828168 | ||
|
|
eadf10f287 | ||
|
|
ec980bf68a | ||
|
|
6f9dffe6e5 | ||
|
|
f61487284f | ||
|
|
483e7c5f51 | ||
|
|
be47d732e0 | ||
|
|
c57110e42a | ||
|
|
e8adaad01f | ||
|
|
4db0f83304 | ||
|
|
2ab23f50b2 | ||
|
|
4df94a4e2b | ||
|
|
97c417b1ad | ||
|
|
c0f194f443 | ||
|
|
e79660c5d1 | ||
|
|
deaf5b7398 | ||
|
|
e4041eabec | ||
|
|
7a1a642dc2 | ||
|
|
555f3a800b | ||
|
|
145784e73c | ||
|
|
f8e492d6cb | ||
|
|
49e04784af | ||
|
|
f843e69a1b | ||
|
|
232ac4317b | ||
|
|
b9dc03cd85 | ||
|
|
1ad7750d8b | ||
|
|
b6c772a326 | ||
|
|
ac5bb63214 | ||
|
|
4d580deeb0 | ||
|
|
cbf337a002 | ||
|
|
15ee6ead49 | ||
|
|
1cc9641f56 | ||
|
|
0cfc3b7ae9 | ||
|
|
fbfe1256a5 | ||
|
|
5074572694 | ||
|
|
e004c34ae3 | ||
|
|
5c0e5c347d | ||
|
|
2935111c6b | ||
|
|
71155eeec5 | ||
|
|
135acb5240 | ||
|
|
5558376bae | ||
|
|
2da560a21f | ||
|
|
bc9a0988a7 | ||
|
|
6a731f986e | ||
|
|
2cb4e551fa | ||
|
|
e5a1844852 | ||
|
|
b8f82ecb5e | ||
|
|
18583f6ce0 | ||
|
|
1468eb1ef2 | ||
|
|
8ab699a910 | ||
|
|
b173311468 | ||
|
|
522dcd8ffb | ||
|
|
6809a35177 | ||
|
|
bd82147496 | ||
|
|
a4224ab1df | ||
|
|
4c4dc8ca46 | ||
|
|
0afabd38c6 | ||
|
|
cacd108802 | ||
|
|
7138243c63 | ||
|
|
73b096a6e5 | ||
|
|
8165a5ff89 | ||
|
|
1017a38eb4 | ||
|
|
80374cae8a | ||
|
|
6339cdffa9 | ||
|
|
96a33bd387 | ||
|
|
1042e599d9 | ||
|
|
1a4f1cb07d | ||
|
|
edb249c2ad | ||
|
|
e9921928df | ||
|
|
fa0c87dec3 | ||
|
|
ba64c2d147 | ||
|
|
cf008b9cff | ||
|
|
3a9069f6e6 | ||
|
|
01abf348d2 | ||
|
|
eae7edbda0 | ||
|
|
d7c6a06b61 | ||
|
|
9d6f7ef0f2 | ||
|
|
0ddb1f69d2 | ||
|
|
4091abdfd7 | ||
|
|
27008cb44a | ||
|
|
b84825eae5 | ||
|
|
1c6ffe8ac6 | ||
|
|
dc0845b2d2 | ||
|
|
7ab502991e | ||
|
|
319f555569 | ||
|
|
0bf323ab97 | ||
|
|
255960d434 | ||
|
|
83a6aed625 | ||
|
|
f1b67049d1 | ||
|
|
8cbca5761e | ||
|
|
a237179a72 | ||
|
|
967846bef5 | ||
|
|
0bf9a2d3da | ||
|
|
18b2d3ac20 | ||
|
|
7d22fe3d7d | ||
|
|
ee67b6c042 | ||
|
|
1cfa76683b | ||
|
|
4662e45e58 | ||
|
|
1fefbdc9ab | ||
|
|
1cb2a57c55 | ||
|
|
246ee83015 | ||
|
|
335ddfc7a5 | ||
|
|
5b500830a1 | ||
|
|
ce4839bd80 | ||
|
|
eee22154e3 | ||
|
|
49007d3915 | ||
|
|
f9b453ca8c | ||
|
|
5f53b81c75 | ||
|
|
9d83adaac8 | ||
|
|
c8c269f612 | ||
|
|
15740904e5 | ||
|
|
85e9ff7a96 | ||
|
|
743617dbda | ||
|
|
89806b828a | ||
|
|
2e530bfdb0 | ||
|
|
83495b3213 | ||
|
|
5108416904 | ||
|
|
5589da1822 | ||
|
|
21e5aed3f3 | ||
|
|
4a88f795e1 | ||
|
|
604fa5be63 | ||
|
|
7ed1be994f | ||
|
|
6cc74a1c0a | ||
|
|
167b303fae | ||
|
|
b91c9e4ea6 | ||
|
|
947fe729cb | ||
|
|
bfb3ee4c43 | ||
|
|
bab1df2c78 | ||
|
|
00eabec771 | ||
|
|
584b7b6f45 | ||
|
|
2312c444ef | ||
|
|
0373d019f8 | ||
|
|
fe09e75b80 | ||
|
|
432c9c6cc1 | ||
|
|
5fc1d55879 | ||
|
|
268cd6cc9c | ||
|
|
20ffe31638 | ||
|
|
02d30067ea | ||
|
|
0ce7808e08 | ||
|
|
d609ee2b7a | ||
|
|
083df61fcc | ||
|
|
a524988be6 | ||
|
|
f2855d6dac | ||
|
|
8517a0b564 | ||
|
|
ec69b8f11b | ||
|
|
4250d0e266 | ||
|
|
edda1b57f3 | ||
|
|
5a7766992d | ||
|
|
02dabe11db | ||
|
|
58024128d8 | ||
|
|
e44bf9d773 | ||
|
|
1114223bda | ||
|
|
55022e5fa6 | ||
|
|
ae732fb298 | ||
|
|
a6ef6996c3 | ||
|
|
97cf5e127b | ||
|
|
690df7373c | ||
|
|
9cc01e5da1 | ||
|
|
0a068a2746 | ||
|
|
85a239ff8c | ||
|
|
a545a28dfb | ||
|
|
ac876a980c | ||
|
|
4b555e8a56 | ||
|
|
1a8d14dc44 | ||
|
|
83263a72d2 | ||
|
|
76f3ba8691 | ||
|
|
072edd7aff | ||
|
|
c0be4d702c | ||
|
|
c3d6db3f1e | ||
|
|
e05ec53eb5 | ||
|
|
8d9dad9c82 | ||
|
|
0071bcb0e4 | ||
|
|
dbdc1ccd1b | ||
|
|
2a606fdb1d | ||
|
|
3b86f30bcf | ||
|
|
7808d028e9 | ||
|
|
04bcf73b8b | ||
|
|
1f8179d519 | ||
|
|
3ffd8946f8 | ||
|
|
c78898e4c1 | ||
|
|
d3e4677ea4 | ||
|
|
d176f9cc0e | ||
|
|
a65366bbe1 | ||
|
|
20cfe74b17 | ||
|
|
b74db81304 | ||
|
|
a8adbda04a | ||
|
|
3e80d30ca6 | ||
|
|
2eb54d50a5 | ||
|
|
6995308c17 | ||
|
|
0738d120ae | ||
|
|
bad6879589 | ||
|
|
b46948c0ba | ||
|
|
3024720fc2 | ||
|
|
cf854c9278 | ||
|
|
608cc2a81f | ||
|
|
396670dc8f | ||
|
|
79f65868ca | ||
|
|
56e5524a0f | ||
|
|
1ae185d0bc | ||
|
|
82276b53a8 | ||
|
|
d5882f16be | ||
|
|
b35e7b0702 | ||
|
|
bce7d95838 | ||
|
|
4d490d0add | ||
|
|
7cff1a9842 | ||
|
|
cfba7b4daa | ||
|
|
4dff48e807 | ||
|
|
8abe0daf20 | ||
|
|
635f069012 | ||
|
|
eeb83f9024 | ||
|
|
b48831e60b | ||
|
|
095791cdc2 | ||
|
|
3e304c497e | ||
|
|
9ffea32176 | ||
|
|
f32c99d4a1 | ||
|
|
1f1b1858c7 | ||
|
|
42fcb91f2e | ||
|
|
01568ca148 | ||
|
|
0d97ad67c5 | ||
|
|
c94a5537a3 | ||
|
|
d88dfc197e | ||
|
|
f709c05c0d | ||
|
|
2c609da7a1 | ||
|
|
d770c7bd08 | ||
|
|
b9ccd4a8a0 | ||
|
|
8875e8059f | ||
|
|
6490366c68 | ||
|
|
c278946771 | ||
|
|
1562d9de36 | ||
|
|
5bb0990abb | ||
|
|
bfd3cacc68 | ||
|
|
c7e1163d59 | ||
|
|
fba7aeb71b | ||
|
|
c66a2bd77a | ||
|
|
ed58ae3c53 | ||
|
|
194463bea9 | ||
|
|
260f5ae777 | ||
|
|
e85d824337 | ||
|
|
e17fc39709 | ||
|
|
da9b297b12 | ||
|
|
807dfcd57a | ||
|
|
0a410db52a | ||
|
|
4501095106 | ||
|
|
bc731e4fb1 | ||
|
|
53561d2755 | ||
|
|
e913fe509c | ||
|
|
a3a215b7ae | ||
|
|
56f2949ac5 | ||
|
|
c1b5a35f90 | ||
|
|
48ab1fbffe | ||
|
|
5f078273b3 | ||
|
|
636338f9ed | ||
|
|
6cbd629777 | ||
|
|
5a07dce759 | ||
|
|
15484bc119 | ||
|
|
d6cd9d136d | ||
|
|
c76815756b | ||
|
|
eef93250ac | ||
|
|
a1c6cf0477 | ||
|
|
14a0ccac93 | ||
|
|
9439743b7e | ||
|
|
acbac6a9c3 | ||
|
|
cc38ccd0e9 | ||
|
|
7d0cf846ac | ||
|
|
f9541fab70 | ||
|
|
8d9870e9c6 | ||
|
|
0f3f8519e0 | ||
|
|
016ae865b1 | ||
|
|
3e9953a74a | ||
|
|
1fc80d2b93 | ||
|
|
b55fe2ef19 | ||
|
|
71030b7e27 | ||
|
|
5e8bdac008 | ||
|
|
56b8c689ec | ||
|
|
454912d314 | ||
|
|
61e3f5761c | ||
|
|
2908569841 | ||
|
|
775226b49f | ||
|
|
e3dacb5b3f | ||
|
|
cdea411136 | ||
|
|
fdb000ee7c | ||
|
|
4a0be1c29d | ||
|
|
892d22e225 | ||
|
|
4958a48b92 | ||
|
|
28bbea85f0 | ||
|
|
73b3a29cfc | ||
|
|
77b8024453 | ||
|
|
1175e1164b | ||
|
|
5546af518e | ||
|
|
99fd3083f2 | ||
|
|
c0df8be832 | ||
|
|
73cab6a6ee | ||
|
|
7a71e45799 | ||
|
|
fdb1d1e6dd | ||
|
|
6dd4d6adeb | ||
|
|
d368f9666a | ||
|
|
ee50458333 | ||
|
|
9792c616b5 | ||
|
|
205a7d134e | ||
|
|
2cbacb4338 | ||
|
|
cdb812ef63 | ||
|
|
ea010f8c9b | ||
|
|
362bbc5f32 | ||
|
|
2e19dda72e | ||
|
|
021155278e | ||
|
|
3db87218ee | ||
|
|
91d5c90eb0 | ||
|
|
f8b71a0e61 | ||
|
|
3e4b7f30ac | ||
|
|
c637985575 | ||
|
|
2960e2459b | ||
|
|
81d6289a86 | ||
|
|
dc1507a5ea | ||
|
|
3d960c3869 | ||
|
|
5d60e6191f | ||
|
|
4b49f8a5a6 | ||
|
|
94459fe922 | ||
|
|
e834e31510 | ||
|
|
4b57a0d729 | ||
|
|
acd440106b | ||
|
|
5096df5cc0 | ||
|
|
3c2d450aa8 | ||
|
|
b083b3cc41 | ||
|
|
6624769032 | ||
|
|
55f75c6051 | ||
|
|
42d9c3ef14 | ||
|
|
c7a9363422 | ||
|
|
969b6e3288 | ||
|
|
03ce030754 | ||
|
|
f2c1e362a0 | ||
|
|
6426aa57a2 | ||
|
|
7ceb0f6306 | ||
|
|
b26a1944c6 | ||
|
|
235aec3e42 | ||
|
|
346fb730a3 | ||
|
|
c87bc22a5f | ||
|
|
7198e24945 | ||
|
|
1a08bd340e | ||
|
|
af9047bf3c | ||
|
|
9566fc4e03 | ||
|
|
41254d10b7 | ||
|
|
ed97f41884 | ||
|
|
02b83ce0ad | ||
|
|
f1d2a1033a | ||
|
|
ba1fe54ef8 | ||
|
|
deac92faf8 | ||
|
|
1c36a79162 | ||
|
|
b6f7042adc | ||
|
|
f721cefb4a | ||
|
|
fc2c947afe | ||
|
|
d0272095cc | ||
|
|
4a09cf289d | ||
|
|
89c23fef35 | ||
|
|
5e59651d45 | ||
|
|
5e72f75395 | ||
|
|
0a77fe0169 | ||
|
|
961abb0f80 | ||
|
|
4efe12d2d3 | ||
|
|
67b05e2d75 | ||
|
|
8edb6f8727 | ||
|
|
b30f02a1fb | ||
|
|
7e2333a63a | ||
|
|
0be66cccbc | ||
|
|
ed26ed196d | ||
|
|
a204f270dd | ||
|
|
7585d7bbd0 | ||
|
|
2981f086c8 | ||
|
|
784ef8a6a4 | ||
|
|
01c91ba294 | ||
|
|
05629acfa9 | ||
|
|
12a3afe15b | ||
|
|
3bb68e3111 | ||
|
|
5ba33be30f | ||
|
|
1e7b057946 | ||
|
|
fb5b00d73f | ||
|
|
09ccfa2624 | ||
|
|
5c80c99b94 | ||
|
|
adcf570b15 | ||
|
|
5ccd6e64bc | ||
|
|
ff7e7858c0 | ||
|
|
37bcd8ce08 | ||
|
|
678b70cee8 | ||
|
|
c0ca8da4ed | ||
|
|
a07dcb1cf5 | ||
|
|
cb4a86d1d5 | ||
|
|
0499347588 | ||
|
|
f4bb459b5e | ||
|
|
83a5a21f95 | ||
|
|
85c633fddf | ||
|
|
cb08e061d2 | ||
|
|
787b52bef7 | ||
|
|
9bf7ca400f | ||
|
|
9f75e30af3 | ||
|
|
3f0243ba9a | ||
|
|
1a9b367c9d | ||
|
|
73996f055b | ||
|
|
57ea2f317a | ||
|
|
c66c05b442 | ||
|
|
6450d2bd56 | ||
|
|
b5368d120d | ||
|
|
f9a310b6c3 | ||
|
|
1bdcfe646f | ||
|
|
a88d14a3bd | ||
|
|
bdb3d09c09 | ||
|
|
667e7b185b | ||
|
|
8483ee0d41 | ||
|
|
c3baaf3ac7 | ||
|
|
c63745d1ba | ||
|
|
59b9ffadd0 | ||
|
|
d131ea3790 | ||
|
|
b849d34be5 | ||
|
|
58fc9a551c | ||
|
|
5801f34b3a | ||
|
|
17cf16ca92 | ||
|
|
7015b1b232 | ||
|
|
3b72ca09c6 | ||
|
|
a815d0245b | ||
|
|
229f22d5a9 | ||
|
|
22f5cfcfd8 | ||
|
|
90ba55c043 | ||
|
|
9f878a353c | ||
|
|
af7297d671 | ||
|
|
2f172b56e9 | ||
|
|
9076c8b20e | ||
|
|
639756dfcd | ||
|
|
7aa0c7e491 | ||
|
|
45dedf5bc7 | ||
|
|
4681ec9008 | ||
|
|
b91826c6e6 | ||
|
|
686856d0ae | ||
|
|
9b09d2578d | ||
|
|
f8f51adf88 | ||
|
|
f8ce639717 | ||
|
|
1c6dc169ac | ||
|
|
3e5366c74e | ||
|
|
b49ddbfef9 | ||
|
|
b92fd73f53 | ||
|
|
41b8f51a6a | ||
|
|
aad9045de5 | ||
|
|
fdd5848df4 | ||
|
|
118c15d046 | ||
|
|
bae4f8e320 | ||
|
|
e0189a566e | ||
|
|
1cd8d73cdb | ||
|
|
d6e9e5987b | ||
|
|
8c5aa37745 | ||
|
|
a18a871ac3 | ||
|
|
90cbff9cf9 | ||
|
|
bae5a04dcc | ||
|
|
7146570392 | ||
|
|
ae88f85d8e | ||
|
|
a362860137 | ||
|
|
c966896522 | ||
|
|
5f88da1985 | ||
|
|
043b80a298 | ||
|
|
ed0da28896 | ||
|
|
61a0d69d58 | ||
|
|
3833a9216e | ||
|
|
e59566b5e2 | ||
|
|
e4be0ce464 | ||
|
|
022dbf0cab | ||
|
|
1e6b559b89 | ||
|
|
74bae2005d | ||
|
|
1731a35d94 | ||
|
|
9f7d766cb3 | ||
|
|
6c0d0b00c9 | ||
|
|
a82a38421d | ||
|
|
c4b01da384 | ||
|
|
3e1473dba5 | ||
|
|
d383706554 | ||
|
|
e0eb0e21f6 | ||
|
|
7266af1749 | ||
|
|
f93bc09438 | ||
|
|
fe3bb7c1b4 | ||
|
|
923676c7d5 | ||
|
|
4755216505 | ||
|
|
37d03c10f9 | ||
|
|
490b724808 | ||
|
|
d8f132919d | ||
|
|
b8a64a6b5b | ||
|
|
25ad1e6f86 | ||
|
|
6b6f1604e9 | ||
|
|
63be1c1cbd | ||
|
|
b75c625ddc | ||
|
|
7083e7aff7 | ||
|
|
9d2937dd4b | ||
|
|
a7e281e278 | ||
|
|
72b6597817 | ||
|
|
91a1b97550 | ||
|
|
9951ab678f | ||
|
|
930aa355e8 | ||
|
|
e0143fa540 | ||
|
|
7c1d92ff4b | ||
|
|
0a0f1e90e1 | ||
|
|
80092823db | ||
|
|
146098d9ce | ||
|
|
519743dbdb | ||
|
|
7ab661ecd7 | ||
|
|
bb46cb08f7 | ||
|
|
028932c04a | ||
|
|
73e6480853 | ||
|
|
aa176b081a | ||
|
|
267243e71b | ||
|
|
33fbef8380 | ||
|
|
45a128a050 | ||
|
|
2ddc668954 | ||
|
|
898bc9b9f2 | ||
|
|
d8e5928523 | ||
|
|
36808a953e | ||
|
|
39d3f79026 | ||
|
|
6463e1ca22 | ||
|
|
c985a13544 | ||
|
|
ad76c5177c | ||
|
|
64b3184b28 | ||
|
|
2f1ad7201f | ||
|
|
cd23ee2055 | ||
|
|
e00830bebc | ||
|
|
00e6d580c2 | ||
|
|
9c7b419e8f | ||
|
|
95edc0d303 | ||
|
|
5991b1e37c | ||
|
|
1aa50cf53a | ||
|
|
eab66e2d19 | ||
|
|
5b504f094f | ||
|
|
1460cb9ac1 | ||
|
|
53782cbf49 | ||
|
|
0ea22dddf0 | ||
|
|
ec466dc818 | ||
|
|
181064901d | ||
|
|
d1988dc982 | ||
|
|
1f1d687317 | ||
|
|
edc7bfc230 | ||
|
|
7b6b3aa293 | ||
|
|
2f7514a2e7 | ||
|
|
575415b93a | ||
|
|
c28dfa8aca | ||
|
|
91141922ee | ||
|
|
cc5154e04e | ||
|
|
77db5ecd12 | ||
|
|
7ac789c9c7 | ||
|
|
24dff05f64 | ||
|
|
64a350364d | ||
|
|
11b7cfe5cb | ||
|
|
71cfcad2a1 | ||
|
|
ab4373b26e | ||
|
|
d23ddc96ac | ||
|
|
147708e779 | ||
|
|
dc969dd7ed | ||
|
|
ef7d1d9327 | ||
|
|
2e6e9ed925 | ||
|
|
296dcab4c7 | ||
|
|
f9e1c46c45 | ||
|
|
94fd5bd7ec | ||
|
|
eb6ca96e85 | ||
|
|
a2bbc7e272 | ||
|
|
f075a991f0 | ||
|
|
edeb817c39 | ||
|
|
23b4658672 | ||
|
|
5f95ee987f | ||
|
|
cc73f156a7 | ||
|
|
ee72d10718 | ||
|
|
831871d37f | ||
|
|
6072550ec1 | ||
|
|
112a565bf7 | ||
|
|
59e5c76286 | ||
|
|
21620ac6bd | ||
|
|
d05129ec67 | ||
|
|
0998de4ae6 | ||
|
|
2bdf1832da | ||
|
|
a846c4b66e | ||
|
|
ee535895a3 | ||
|
|
1e549dfd43 | ||
|
|
6ee718a252 | ||
|
|
557e98c33f | ||
|
|
7a9eec88e8 | ||
|
|
a7a4f66633 | ||
|
|
a88d0a6ae1 | ||
|
|
db87bc770e | ||
|
|
7b6b71cd4b | ||
|
|
df98463325 | ||
|
|
f7492db8bd | ||
|
|
70b46d4a8f | ||
|
|
411486e1e7 | ||
|
|
6f81305232 | ||
|
|
79bc22d8ce | ||
|
|
1c634a702a | ||
|
|
909a9e4050 | ||
|
|
b5cc794061 | ||
|
|
73b8e85976 | ||
|
|
282b5d6893 | ||
|
|
c6628e7311 | ||
|
|
6b109d172f | ||
|
|
6b29972399 | ||
|
|
0fcd3c09fd | ||
|
|
af503442b8 | ||
|
|
c875971b71 | ||
|
|
d1a65922d7 | ||
|
|
6ef34f95d5 | ||
|
|
8b79022179 | ||
|
|
21aec77e5c | ||
|
|
74c5259af8 | ||
|
|
a3e7d4414d | ||
|
|
986d32eb81 | ||
|
|
de0ae14544 | ||
|
|
6b52276fb6 | ||
|
|
a19ea7489c | ||
|
|
14229c2f00 | ||
|
|
6eb20a1f2e | ||
|
|
8debac2edf | ||
|
|
a68301e4dc | ||
|
|
c6a988bc92 | ||
|
|
fe02ce7b64 | ||
|
|
df012dec90 | ||
|
|
5969425a6f | ||
|
|
b17b1e6463 | ||
|
|
c99e61c402 | ||
|
|
f4aaec8b3c | ||
|
|
adc3e6118b | ||
|
|
d933493c31 | ||
|
|
f91d591b03 | ||
|
|
af6deb99cd | ||
|
|
c5d285f755 | ||
|
|
b1eb706925 | ||
|
|
5a01634ca3 | ||
|
|
487676ce13 | ||
|
|
0280ca7b1a | ||
|
|
b0ccab41e1 | ||
|
|
ccda3a3325 | ||
|
|
4b7eeaa6e0 | ||
|
|
2951f0030d | ||
|
|
acc2df29de | ||
|
|
431afd618f | ||
|
|
6d5e5bd692 | ||
|
|
ffd2e8149e | ||
|
|
2ab92dc13e | ||
|
|
7f6a8bc87e | ||
|
|
b1ff163a28 | ||
|
|
440d55ccb8 | ||
|
|
285532d431 | ||
|
|
f2c47459f8 | ||
|
|
49703f08e5 | ||
|
|
1d0aa9573b | ||
|
|
b2014cf88b | ||
|
|
a0e0078bad | ||
|
|
5ebca21c32 | ||
|
|
970aea90c9 | ||
|
|
5f9341ad8e | ||
|
|
574c0983f5 | ||
|
|
be6c7c7ac8 | ||
|
|
4fd31f276b | ||
|
|
224db7da57 | ||
|
|
1413e1aff4 | ||
|
|
68b669d3ff | ||
|
|
29f44c67c8 | ||
|
|
3332d2288f | ||
|
|
34702196e1 | ||
|
|
d45c8d1e9b | ||
|
|
bc19825ada | ||
|
|
72bb640239 | ||
|
|
aacee4a94c | ||
|
|
3ab37d5c5d | ||
|
|
3dd3ecf8f1 | ||
|
|
a3831827d0 | ||
|
|
6aa6c957ee | ||
|
|
9e12412f5f | ||
|
|
0f9eb31740 | ||
|
|
6be8ab581d | ||
|
|
d8425bc9c5 | ||
|
|
0ddcb9c00a | ||
|
|
6d43623f45 | ||
|
|
196cd88010 | ||
|
|
7f37df4227 | ||
|
|
985a12a63b | ||
|
|
9058c0e9fc | ||
|
|
4b0cd32d12 | ||
|
|
6cb51bc55d | ||
|
|
119e3c31c9 | ||
|
|
56164c25d0 | ||
|
|
c66e5f9fcd | ||
|
|
12700e1754 | ||
|
|
50db6f0765 | ||
|
|
64e8adddfd | ||
|
|
729a4d64e9 | ||
|
|
6f12504588 | ||
|
|
271459f820 | ||
|
|
5000c95d01 | ||
|
|
f477733483 | ||
|
|
54e1681c5e | ||
|
|
2f6d9a156a | ||
|
|
10dd89ae62 | ||
|
|
d01bfbec96 | ||
|
|
5eb4aa3a0e | ||
|
|
0b9933df1e | ||
|
|
76d12d6062 | ||
|
|
cf10faf61c | ||
|
|
1cbf9c1cd9 | ||
|
|
25e361b9f9 | ||
|
|
b88ee33ae4 | ||
|
|
684964da4f | ||
|
|
8a3841f638 | ||
|
|
f642e42eea | ||
|
|
bbef854c02 | ||
|
|
e50611666e | ||
|
|
eae4f721e8 | ||
|
|
12fed34e10 | ||
|
|
56350b54ee | ||
|
|
10b7644bb7 | ||
|
|
d79db3bd3f | ||
|
|
1588461633 | ||
|
|
dd999b60a4 | ||
|
|
3abee72fee | ||
|
|
b5577b1d37 | ||
|
|
e15ffb5820 | ||
|
|
4d9a5ed4a1 | ||
|
|
b2bc1debe0 | ||
|
|
590ff67fcb | ||
|
|
209e1adf53 | ||
|
|
53c08484a3 | ||
|
|
c6ca832737 | ||
|
|
2c399a078e | ||
|
|
8c519f13da | ||
|
|
853fdc70a2 | ||
|
|
dc4f811eaa | ||
|
|
d23c8b4a2a | ||
|
|
00c0dcc81d | ||
|
|
f77feefdb8 | ||
|
|
2e346e5369 | ||
|
|
17023f6b55 | ||
|
|
3bb29abe32 | ||
|
|
ac42d38b7a | ||
|
|
d9c0130b59 | ||
|
|
4925d5a5e7 | ||
|
|
dd9a7cf5d7 | ||
|
|
5ee3874b7e | ||
|
|
17dd77cc96 | ||
|
|
6c546b5290 | ||
|
|
a853fc2026 | ||
|
|
92c9ac3826 | ||
|
|
78c2ced43b | ||
|
|
72f850f675 | ||
|
|
bc326489ab | ||
|
|
ea5e7d9563 | ||
|
|
5b5b48fc06 | ||
|
|
1548ba0b8d | ||
|
|
26b1c4244f | ||
|
|
8a4e981931 | ||
|
|
6163c3f08e | ||
|
|
e17f381b1f | ||
|
|
316537eb4d | ||
|
|
b2c421600c | ||
|
|
787f6ef528 | ||
|
|
8578547467 | ||
|
|
51ab6d6da1 | ||
|
|
3a8b5de8f7 | ||
|
|
faf08f6513 | ||
|
|
06c69d23be | ||
|
|
1bcadd5f8e | ||
|
|
524195d729 | ||
|
|
0c25d277ef | ||
|
|
27b0348e1d | ||
|
|
ea5aa68769 | ||
|
|
99fefb168a | ||
|
|
49457505cd | ||
|
|
bfc948a9b4 | ||
|
|
c407206627 | ||
|
|
39d3bf97d1 | ||
|
|
79be392775 | ||
|
|
be4c6b8e16 | ||
|
|
c8e193e70d | ||
|
|
35859ffc3f | ||
|
|
0d81ada5a8 | ||
|
|
b68cf0fb45 | ||
|
|
9ed2078e92 | ||
|
|
1f002159e2 | ||
|
|
5bc690fcd9 | ||
|
|
bab9adce24 | ||
|
|
e47eddaa85 | ||
|
|
8ef1f2e395 | ||
|
|
541131bbc6 | ||
|
|
7626eecbf6 | ||
|
|
49afa75929 | ||
|
|
5c5265ede2 | ||
|
|
42d61d8089 | ||
|
|
01eb50078e | ||
|
|
eef021f472 | ||
|
|
6f3fd785e7 | ||
|
|
7cd8a645a8 | ||
|
|
9671348dc1 | ||
|
|
7a3e68d656 | ||
|
|
42c7ec2f75 | ||
|
|
32c3ce5c98 | ||
|
|
e55a3a82fc | ||
|
|
305da86f97 | ||
|
|
c23d1d11b5 | ||
|
|
a3cabd5f36 | ||
|
|
66ac4716f2 | ||
|
|
3cd1aaeb03 | ||
|
|
4eb940ffe7 | ||
|
|
61800b23e2 | ||
|
|
0283662931 | ||
|
|
d125eb56b3 | ||
|
|
956d68695c | ||
|
|
83d81b64b3 | ||
|
|
a4ea82c04b | ||
|
|
8387fe0d5b | ||
|
|
cfd5b388f1 | ||
|
|
4ee6e38a94 | ||
|
|
3f87752d1f | ||
|
|
b91548eef4 | ||
|
|
1195417b97 | ||
|
|
67f347197e | ||
|
|
8c2dfa9140 | ||
|
|
a3fbfe0bff | ||
|
|
99db1b1cc3 | ||
|
|
638a7f0ab4 | ||
|
|
806a69fef3 | ||
|
|
8ba2e9e34c | ||
|
|
e7e54bc19e | ||
|
|
9fb980599f | ||
|
|
28dfef985c | ||
|
|
1e416b9f8a | ||
|
|
784bcb0aa5 | ||
|
|
9642df2d9d | ||
|
|
37340838b6 | ||
|
|
d1a8dd7817 | ||
|
|
8919a3937a | ||
|
|
5032030f8d | ||
|
|
b30cb5d7dc | ||
|
|
7113c4622b | ||
|
|
bd8caff0b7 | ||
|
|
519bf3184a | ||
|
|
79c77ce3a3 | ||
|
|
2f40f795ee | ||
|
|
b16f92314b | ||
|
|
ad22244388 | ||
|
|
02f89a9c9d | ||
|
|
d286c040a5 | ||
|
|
99f5b8ebc1 | ||
|
|
9ac33f9b9b | ||
|
|
6ab1fcaf89 | ||
|
|
2e3d0cc57c | ||
|
|
1e44115461 | ||
|
|
8d57063e9d | ||
|
|
104d646c7c | ||
|
|
9ddbf79d9e | ||
|
|
a9ec4c5c28 | ||
|
|
914d860197 | ||
|
|
23b3e5c731 | ||
|
|
cdf04c2402 | ||
|
|
3535e44337 | ||
|
|
0b245d3885 | ||
|
|
4fda6cbcde | ||
|
|
2bbba897ec | ||
|
|
0cfb94b0ba | ||
|
|
3f7ac93932 | ||
|
|
96c36b4f6a | ||
|
|
febd6d32cf | ||
|
|
cbd8699801 | ||
|
|
1ee1d61c74 | ||
|
|
74400aacc6 | ||
|
|
9f55c3605a | ||
|
|
8d61e8179f | ||
|
|
f250889c3e | ||
|
|
00f67d86d6 | ||
|
|
7b8b9cfd2b | ||
|
|
5e7a67834b | ||
|
|
3c85602ab1 | ||
|
|
66d0d0e213 | ||
|
|
1f68faddb9 | ||
|
|
db06f06c96 | ||
|
|
5b580d2a17 | ||
|
|
083dd7d1a3 | ||
|
|
03bd4755ce | ||
|
|
79e973e9c8 | ||
|
|
29d37075dd | ||
|
|
f311bac580 | ||
|
|
beb7a4c992 | ||
|
|
4d86fb319b | ||
|
|
5ea4f46de7 | ||
|
|
1d8d5251ae | ||
|
|
54c8217808 | ||
|
|
ba623903e0 | ||
|
|
907af3ae18 | ||
|
|
24ae8a6b66 | ||
|
|
1646a5cdd2 | ||
|
|
814f17d10b | ||
|
|
40fe105903 | ||
|
|
42a347d8b1 | ||
|
|
5450e5dac4 | ||
|
|
1368259a1e | ||
|
|
81a495f267 | ||
|
|
693a4a6633 | ||
|
|
82786c580a | ||
|
|
e19743f705 | ||
|
|
9166a57930 | ||
|
|
8d8304e859 | ||
|
|
6ddc23e2aa | ||
|
|
2fc491015e | ||
|
|
bb0afe1fa7 | ||
|
|
eb46f8c776 | ||
|
|
bd511f97cb | ||
|
|
560bf40e4b | ||
|
|
4f4652c1cd | ||
|
|
60e13c2a1d | ||
|
|
1fe0dc4d16 | ||
|
|
181a1e3c0a | ||
|
|
6bba771856 | ||
|
|
921f1f42fb | ||
|
|
eeb1f27fa4 | ||
|
|
9ce21ad152 | ||
|
|
c036929cfe | ||
|
|
21591a3a89 | ||
|
|
a2e9a41a7e | ||
|
|
0902349130 | ||
|
|
f900db8e10 | ||
|
|
0fa9b344e0 | ||
|
|
f48ef3d17b | ||
|
|
40801d0a06 | ||
|
|
c6ccf1cf21 | ||
|
|
d311992983 | ||
|
|
b4babbe2c7 | ||
|
|
0719f4c99e | ||
|
|
eb5de15033 | ||
|
|
b229486d3b | ||
|
|
33b8d3e219 | ||
|
|
230256793f | ||
|
|
540ef96745 | ||
|
|
1baf30a671 | ||
|
|
5e93840e48 | ||
|
|
73a5908039 | ||
|
|
8429148273 | ||
|
|
17f40e2180 | ||
|
|
0345b12379 | ||
|
|
163b0de874 | ||
|
|
1661caed05 | ||
|
|
1089aeab9e | ||
|
|
1a0d3eeb1b | ||
|
|
c27636529d | ||
|
|
ac85488245 | ||
|
|
433e98b645 | ||
|
|
873654669e | ||
|
|
8b96f218d5 | ||
|
|
52cbff0e15 | ||
|
|
32de8d9ccb | ||
|
|
e06da886f7 | ||
|
|
cdd5ad3a8e | ||
|
|
9bee0e460b | ||
|
|
60c8ace443 | ||
|
|
933aaeaf25 | ||
|
|
ca43c77525 | ||
|
|
b204182c13 | ||
|
|
dfb4165a12 | ||
|
|
26a54cd228 | ||
|
|
e229f14121 | ||
|
|
ee6cdfb391 | ||
|
|
58354e563c | ||
|
|
48f1bf0918 | ||
|
|
0ffe1f2cef | ||
|
|
8fe1adacf3 | ||
|
|
30245c5d8a | ||
|
|
67a5225bde | ||
|
|
58c3d7087b | ||
|
|
c408687af7 | ||
|
|
33b284afc0 | ||
|
|
78004bdfb5 | ||
|
|
640950d4c8 | ||
|
|
998de0f9a0 | ||
|
|
ce6e515309 | ||
|
|
e054c8fc55 | ||
|
|
9fa1c2eb3e | ||
|
|
64a314c19e | ||
|
|
40be42406c | ||
|
|
bca0eefc83 | ||
|
|
662ca19f8f | ||
|
|
65f9d482f3 | ||
|
|
7058d5cb93 | ||
|
|
f7b13c69e9 | ||
|
|
9f21b1a097 | ||
|
|
53983002b6 | ||
|
|
d3c0914ac1 | ||
|
|
7eb9694221 | ||
|
|
b8f0d37420 | ||
|
|
1d5b1c239c | ||
|
|
d1ebc08478 | ||
|
|
5074a91669 | ||
|
|
40e56c4040 | ||
|
|
3b0ed9310a | ||
|
|
f92dc6a1ad | ||
|
|
0040b76a19 | ||
|
|
6c6fbabf14 | ||
|
|
8c2d868093 | ||
|
|
96c9e74c6f | ||
|
|
d947437c10 | ||
|
|
83df29d832 | ||
|
|
607afe864a | ||
|
|
a97cee84f3 | ||
|
|
ad64384891 | ||
|
|
f75c73d739 | ||
|
|
418bcddc95 | ||
|
|
61192b998a | ||
|
|
5ea2b09dc3 | ||
|
|
5bfc2c4a9b | ||
|
|
8ec47c3894 | ||
|
|
f4423638a2 | ||
|
|
7b3444308b | ||
|
|
5ec9916817 | ||
|
|
be1a70299f | ||
|
|
8685aa371a | ||
|
|
0224faa184 | ||
|
|
8546e326cf | ||
|
|
9956fd2f04 | ||
|
|
4f669ca82f | ||
|
|
1cd3881aa8 | ||
|
|
e634513f7b | ||
|
|
7b6cde6ae3 | ||
|
|
18146fdf9e | ||
|
|
2c80c35b21 | ||
|
|
54b73769b8 | ||
|
|
f7983ee4d9 | ||
|
|
9eace86aee | ||
|
|
2fbb58eb2b | ||
|
|
187d04e3a1 | ||
|
|
d5d7d73440 | ||
|
|
b747e281b7 | ||
|
|
e024d50476 | ||
|
|
a6ba48c075 | ||
|
|
e19375387d | ||
|
|
a9f68187d4 | ||
|
|
4d754fa78d | ||
|
|
6d07ab2bc5 | ||
|
|
a60b00c440 | ||
|
|
d0f3f303b6 | ||
|
|
4fc8acce8c | ||
|
|
0797a4f99d | ||
|
|
db453c8038 | ||
|
|
c776c34cfd | ||
|
|
170b39fde6 | ||
|
|
fc27a66825 | ||
|
|
06b49c140e | ||
|
|
3ab45c91e1 | ||
|
|
6660161cec | ||
|
|
8c6e207008 | ||
|
|
4180e3c540 | ||
|
|
a218cd0ffb | ||
|
|
31c8de5bbe | ||
|
|
faac7f365f | ||
|
|
8cc4332b10 | ||
|
|
abd30da102 | ||
|
|
f4701ff72a | ||
|
|
393d7885a1 | ||
|
|
b4c6d3c975 | ||
|
|
3ce440a28d | ||
|
|
cb4ab6a99f | ||
|
|
6c39d7b1ee | ||
|
|
840bd52671 | ||
|
|
f3d70c7ea1 | ||
|
|
22b8528ee1 | ||
|
|
900e302df7 | ||
|
|
be03d8e137 | ||
|
|
d338a9639a | ||
|
|
235972f3da | ||
|
|
ae822881e7 | ||
|
|
26f75c71ba | ||
|
|
ad2aa2eff5 | ||
|
|
52689049ae | ||
|
|
1a29541140 | ||
|
|
ece17eecef | ||
|
|
2b353094eb | ||
|
|
70305aa501 | ||
|
|
22dc504ad0 | ||
|
|
ef9402d403 | ||
|
|
f7e29532f7 | ||
|
|
60770683b6 | ||
|
|
051bbbc64f | ||
|
|
206eef964c | ||
|
|
b8b7adff17 | ||
|
|
42c68d362e | ||
|
|
1df3967889 | ||
|
|
dae87e26a3 | ||
|
|
f193341eae | ||
|
|
d21a042ad8 | ||
|
|
b16b9e813d | ||
|
|
a79fe1f350 | ||
|
|
14f99875fb | ||
|
|
2d2890b34f | ||
|
|
66d8cafd76 | ||
|
|
468ccbf2b7 | ||
|
|
6b6668f73b | ||
|
|
5eda05f007 | ||
|
|
1d6a8bd851 | ||
|
|
e500af1ed4 | ||
|
|
7ee39fd4ed | ||
|
|
9ba6c83821 | ||
|
|
4243622414 | ||
|
|
8374a4f5bf | ||
|
|
5d851141cb | ||
|
|
26ac081182 | ||
|
|
64e0d9a4d5 | ||
|
|
269a6cad80 | ||
|
|
806ebdb8a8 | ||
|
|
ed0016fd2b | ||
|
|
a3fb24993d | ||
|
|
9c26598831 | ||
|
|
7634f153b7 | ||
|
|
cabc4da3ac | ||
|
|
6419539305 | ||
|
|
97e1178525 | ||
|
|
04faf12c14 | ||
|
|
e7aa79cc9f | ||
|
|
f862e3f37f | ||
|
|
ce051af5bb | ||
|
|
dcb9f1c840 | ||
|
|
83d0fe9f09 | ||
|
|
7789ba4d36 | ||
|
|
80101b04e2 | ||
|
|
310d4d1b57 | ||
|
|
126c0c6ad6 | ||
|
|
6a43b44087 | ||
|
|
6772b32609 | ||
|
|
db2f0b8c5c | ||
|
|
1376004197 | ||
|
|
1625989c48 | ||
|
|
f7863bd686 | ||
|
|
883565905a | ||
|
|
786780ce9b | ||
|
|
d2e9fed62d | ||
|
|
0e5a4fb098 | ||
|
|
f223f042de | ||
|
|
cd413825ed | ||
|
|
a851c272cd | ||
|
|
412077b418 | ||
|
|
4df6f8a50e | ||
|
|
44bf4b1cc1 | ||
|
|
136e8dd7c5 | ||
|
|
024b2b04a4 | ||
|
|
5bbf210394 | ||
|
|
039c62b09b | ||
|
|
07f0aa45ef | ||
|
|
de11f44309 | ||
|
|
acee96ef17 | ||
|
|
4ed49f9dfa | ||
|
|
f68b585f8f | ||
|
|
13ddc979ec | ||
|
|
b0b7ac3efb | ||
|
|
62f8525dd5 | ||
|
|
1347355cb1 | ||
|
|
f847c4a414 | ||
|
|
776fa924e3 | ||
|
|
8872466968 | ||
|
|
b620038d98 | ||
|
|
a248367b15 | ||
|
|
c159ec4a9a | ||
|
|
5359a7670f | ||
|
|
7e1c7a6de2 | ||
|
|
91e19bbdd3 | ||
|
|
e61daaee2d | ||
|
|
8caab1fd92 | ||
|
|
cd944882c3 | ||
|
|
888d9591fe | ||
|
|
833808c5de | ||
|
|
d731956b06 | ||
|
|
40449ae4de | ||
|
|
44ad61f004 | ||
|
|
74865d53f8 | ||
|
|
373415261e | ||
|
|
d0f653da9a | ||
|
|
cbb8319cfa | ||
|
|
0e467a6024 | ||
|
|
e505916525 | ||
|
|
31f09ab117 | ||
|
|
09e5e0f9b3 | ||
|
|
773cada57a | ||
|
|
403947ed6d | ||
|
|
d9d08a725c | ||
|
|
e2ed75af94 | ||
|
|
dd19afce92 | ||
|
|
5b5deac7d9 | ||
|
|
3f3ee3456e | ||
|
|
3e2f2fc02e | ||
|
|
c5a3003cf7 | ||
|
|
4c6dcddf11 | ||
|
|
b314e500cd | ||
|
|
b83e6ad13f | ||
|
|
fee401cfdf | ||
|
|
fa14f62198 | ||
|
|
5526665494 | ||
|
|
6249af996a | ||
|
|
e51a1b365e | ||
|
|
f53f00d126 | ||
|
|
ab8fbaf21d | ||
|
|
63d8bcf882 | ||
|
|
e4e16bc6a6 | ||
|
|
e4c21c4d5c | ||
|
|
d9e6dbf889 | ||
|
|
5f4469e306 | ||
|
|
16f6365b18 | ||
|
|
cdab54bf51 | ||
|
|
b6fea0c856 | ||
|
|
6f186932cc | ||
|
|
de544ec725 | ||
|
|
a6c0d2c6f1 | ||
|
|
437d956cad | ||
|
|
43ba0b9da6 | ||
|
|
fe1e2c3b62 | ||
|
|
bbe7e5f96d | ||
|
|
2bfad9fc65 | ||
|
|
9f24c18f7f | ||
|
|
a2d1e5ea03 | ||
|
|
b082e4e988 | ||
|
|
45fbce0c2a | ||
|
|
ff7006e232 | ||
|
|
c68fdef0e4 | ||
|
|
4c60e4edc1 | ||
|
|
f2e4e59f8d | ||
|
|
898205b5b1 | ||
|
|
8ec6862861 | ||
|
|
c3ba6322d8 | ||
|
|
e589828425 | ||
|
|
c909aa161b | ||
|
|
5cee7d44f1 | ||
|
|
973b323a99 | ||
|
|
d55954a363 | ||
|
|
adca151e4f | ||
|
|
43513049be | ||
|
|
a5ca41131b | ||
|
|
2ea3810980 | ||
|
|
c9cb54e8b2 | ||
|
|
23dd3db50b | ||
|
|
179c46914d | ||
|
|
ddb18e6c21 | ||
|
|
d2e147ba51 | ||
|
|
b63033f846 | ||
|
|
677e1101e6 | ||
|
|
3abc2ccfbb | ||
|
|
827d28f1cd | ||
|
|
59d12a1bbe | ||
|
|
8134172301 | ||
|
|
8d983aa561 | ||
|
|
bb3085ef84 | ||
|
|
78b1650bdb | ||
|
|
5edc72d475 | ||
|
|
1df32c9dfa | ||
|
|
bedb1ff7f9 | ||
|
|
fbf12f16b5 | ||
|
|
22a5f34e1f | ||
|
|
e1a8b08619 | ||
|
|
466d30fb74 | ||
|
|
7a1c6d2918 | ||
|
|
9fcc0dc8e7 | ||
|
|
286f244caf | ||
|
|
52ebeab90b | ||
|
|
6be7591332 | ||
|
|
73325aaefb | ||
|
|
0adcc6a8d1 | ||
|
|
93fb6acd1d | ||
|
|
77d52b323d | ||
|
|
ca8e8bf6ef | ||
|
|
9acac86ed5 | ||
|
|
ba5007219d | ||
|
|
ec046fd599 | ||
|
|
5452ff1153 | ||
|
|
d03b1e0608 | ||
|
|
53c88ad5af | ||
|
|
21585ca565 | ||
|
|
2fabee647a | ||
|
|
cf4632045c | ||
|
|
ec75afbc44 | ||
|
|
c7b298c46f | ||
|
|
3406bb5a4a | ||
|
|
e9427b4694 | ||
|
|
517a1f1835 | ||
|
|
6e735bbd1e | ||
|
|
5a148aa3b9 | ||
|
|
b4c362da37 | ||
|
|
575ae164c8 | ||
|
|
a9606bfb4e | ||
|
|
b5ec04723d | ||
|
|
51cc08411f | ||
|
|
d75034deae | ||
|
|
4ce23debb6 | ||
|
|
063706a7bf | ||
|
|
eb41a3655f | ||
|
|
a84476187f | ||
|
|
70b603d601 | ||
|
|
c9709f2698 | ||
|
|
be4f479afd | ||
|
|
a251465dbc | ||
|
|
9b7051f2be | ||
|
|
8bfdef79c4 | ||
|
|
f2c2bf81b3 | ||
|
|
8b5247b9bb | ||
|
|
075b1dc0eb | ||
|
|
42e1f0478d | ||
|
|
d4653678b2 | ||
|
|
a4ce752e58 | ||
|
|
f6649398ef | ||
|
|
02859cc270 | ||
|
|
4ed30e082f | ||
|
|
d3985dd129 | ||
|
|
ac70821fea | ||
|
|
38b273a1c9 | ||
|
|
eb5c88fbb2 | ||
|
|
1102952b47 | ||
|
|
5ad6cadcee | ||
|
|
5d236808d6 | ||
|
|
ada9243e84 | ||
|
|
ad4e1c1b5b | ||
|
|
c5105c29b0 | ||
|
|
f689b0f3b2 | ||
|
|
730f614024 | ||
|
|
2e4eb17a48 | ||
|
|
55d2a1f09b | ||
|
|
e3a5bcb907 | ||
|
|
d56567c9de | ||
|
|
d7c381e05d | ||
|
|
1d23dd2426 | ||
|
|
86ce00adf9 | ||
|
|
e1eef013a8 | ||
|
|
d20046c866 | ||
|
|
2df452fe5b | ||
|
|
c31bfd8b94 | ||
|
|
f443675f4f | ||
|
|
a44bd8849d | ||
|
|
274c887140 | ||
|
|
44973ebd00 | ||
|
|
88f74163ff | ||
|
|
6cd57dd426 | ||
|
|
481e866011 | ||
|
|
a78450ba79 | ||
|
|
9fcdeca692 | ||
|
|
8e10c56304 | ||
|
|
591f600b11 | ||
|
|
af03e55a73 | ||
|
|
1462cddd1e | ||
|
|
aac569a925 | ||
|
|
d19ac1fd15 | ||
|
|
410a23751b | ||
|
|
8190507e8c | ||
|
|
645f74f39d | ||
|
|
acdf0912d4 | ||
|
|
32e4e91ab8 | ||
|
|
b59ca329f3 | ||
|
|
beb9099bdc | ||
|
|
a013d95f0f | ||
|
|
9d5daf0015 | ||
|
|
1146307736 | ||
|
|
c25eaadc1d | ||
|
|
50f6e76ab9 | ||
|
|
c3637e731f | ||
|
|
c31eef6b82 | ||
|
|
802683b765 | ||
|
|
335cf93970 | ||
|
|
041954c067 | ||
|
|
2da44c3699 | ||
|
|
65e53092e8 | ||
|
|
0203aa2b6e | ||
|
|
f83fe28a18 | ||
|
|
e487b45898 | ||
|
|
4a94eab393 | ||
|
|
5ff7e6ef0e | ||
|
|
0c99f41bd9 | ||
|
|
bcac810f71 | ||
|
|
feae105426 | ||
|
|
d46b9c54b1 | ||
|
|
d0b7162b6a | ||
|
|
c16660254b | ||
|
|
bbe0d52740 | ||
|
|
65117ebdd7 | ||
|
|
445d55e800 | ||
|
|
dbce751464 | ||
|
|
b8640d903f | ||
|
|
6e7560ee77 | ||
|
|
efa26a067f | ||
|
|
f7cf7c198d | ||
|
|
d32f4fc38e | ||
|
|
0c8b8647f3 | ||
|
|
c38dbbb1d7 | ||
|
|
98cec15625 | ||
|
|
bad9828f47 | ||
|
|
18f91ddffa | ||
|
|
335745d365 | ||
|
|
4204b31398 | ||
|
|
029a568645 | ||
|
|
6b2f1fcd3e | ||
|
|
3bdc610249 | ||
|
|
c03a70fde2 | ||
|
|
c77645e173 | ||
|
|
6531002d61 | ||
|
|
fea808ca5f | ||
|
|
59ba408070 | ||
|
|
3a8931feef | ||
|
|
7ebd8f6bf5 | ||
|
|
73883979c6 | ||
|
|
d8935b46b3 | ||
|
|
1505d04622 | ||
|
|
3b690cc31f | ||
|
|
b3814920bd | ||
|
|
8bf1f828b9 | ||
|
|
911e69e3bc | ||
|
|
77b4a1eaf6 | ||
|
|
2ed12c429e | ||
|
|
e578c52fdf | ||
|
|
5ff4e3c4ea | ||
|
|
1c2e7256c1 | ||
|
|
7a51ca225a | ||
|
|
8d242d8072 | ||
|
|
543b068efa | ||
|
|
eadbd5e821 | ||
|
|
c771f5a13c | ||
|
|
f13b3111c3 | ||
|
|
bb2714ff24 | ||
|
|
54c42b1fc2 | ||
|
|
1f42f933f0 | ||
|
|
6c533d225b | ||
|
|
67a89d1289 | ||
|
|
0b9bef2f38 | ||
|
|
1c4649409d | ||
|
|
e1daaf07ce | ||
|
|
cd21f2d1d7 | ||
|
|
836e41064f | ||
|
|
4658e4c739 | ||
|
|
7993a7cdb0 | ||
|
|
567cb7d737 | ||
|
|
985128b537 | ||
|
|
cd35568e04 | ||
|
|
f612509cac | ||
|
|
f415190483 | ||
|
|
e00733a346 | ||
|
|
aafafa0e73 | ||
|
|
c87c9af12e | ||
|
|
622215715f | ||
|
|
c87250c028 | ||
|
|
d6b6d700a5 | ||
|
|
9d4e2c98a3 | ||
|
|
08094c2660 | ||
|
|
fda82c82b0 | ||
|
|
f0eabd4ea0 | ||
|
|
5a4d812146 | ||
|
|
bf156a13bd | ||
|
|
53d276a8fc | ||
|
|
978fa54518 | ||
|
|
31f82e58b5 | ||
|
|
5967f66e6d | ||
|
|
bea81b54ca | ||
|
|
6fd403bdca | ||
|
|
3d673d9d40 | ||
|
|
cac949de56 | ||
|
|
dffa152698 | ||
|
|
7796298fca | ||
|
|
5291bfe8d4 | ||
|
|
b364313297 | ||
|
|
2bef608e07 | ||
|
|
aee13ad909 | ||
|
|
9d82eba599 | ||
|
|
4852beb390 | ||
|
|
522c2f61c0 | ||
|
|
d331396afe | ||
|
|
c725cee044 | ||
|
|
367ef4ecb2 | ||
|
|
c3a64facd5 | ||
|
|
2671781e1b | ||
|
|
9291fa68aa | ||
|
|
6ebb3659f4 | ||
|
|
109696e965 | ||
|
|
b86bbd370c | ||
|
|
1575a4fb1a | ||
|
|
e2f500be90 | ||
|
|
284b00a826 | ||
|
|
66180e19b5 | ||
|
|
1531462d22 | ||
|
|
e17cd1f298 | ||
|
|
13092e9f80 | ||
|
|
8133b8b9dd | ||
|
|
e25aafac6d | ||
|
|
037c7beb1b | ||
|
|
faac4dfc30 | ||
|
|
469a088a4d | ||
|
|
9c854f727f | ||
|
|
8f6e5bd24b | ||
|
|
992f91cf4c | ||
|
|
0c61d4c978 | ||
|
|
72d32edf9a | ||
|
|
dde39def9e | ||
|
|
6b43007c44 | ||
|
|
876558cf77 | ||
|
|
b35a146edf | ||
|
|
86cf6a9908 | ||
|
|
b0f7288ac0 | ||
|
|
32fcc1a8fb | ||
|
|
eb4d125eaf | ||
|
|
87e5cced3c | ||
|
|
bcd9ee2d48 | ||
|
|
edf3d87458 | ||
|
|
0c0c353ecc | ||
|
|
aaa4c8f899 | ||
|
|
57e3565c11 | ||
|
|
fbcf72d762 | ||
|
|
ca8daa836e | ||
|
|
c2ccdbec9d | ||
|
|
69aee36e75 | ||
|
|
063f5c3b55 | ||
|
|
6d1b8ca65e | ||
|
|
03899d4d9c | ||
|
|
2b84af977d | ||
|
|
e15b180322 | ||
|
|
f070030f6b | ||
|
|
330ac66b38 | ||
|
|
12a9e650af | ||
|
|
1e5ccd811e | ||
|
|
2902ee6ad5 | ||
|
|
90ce4fec2c | ||
|
|
a7ab26d08d | ||
|
|
dcc396afb7 | ||
|
|
3f1722d54d | ||
|
|
c79658afbb | ||
|
|
6f84ebb323 | ||
|
|
54c8d62243 | ||
|
|
83e6476408 | ||
|
|
03f317ffdb | ||
|
|
3f67c7c74a | ||
|
|
b8b4660563 | ||
|
|
3d42bfd479 | ||
|
|
c4ebbaba74 | ||
|
|
2ae193092d | ||
|
|
7e1d52ff00 | ||
|
|
c98f43b984 | ||
|
|
e93f128a7a | ||
|
|
71d8e7edd2 | ||
|
|
48f4298a8d | ||
|
|
1c15beadc7 | ||
|
|
2c1600ddfb | ||
|
|
298f7d9d52 | ||
|
|
105f0bfde2 | ||
|
|
cf3a78e114 | ||
|
|
9cc5f0f889 | ||
|
|
81e588a896 | ||
|
|
e30db9ee77 | ||
|
|
235be757f8 | ||
|
|
e31d26a887 | ||
|
|
2293ba02ea | ||
|
|
7188997dd1 | ||
|
|
31cfb09468 | ||
|
|
b76f2e2008 | ||
|
|
4b90972341 | ||
|
|
f4ff34224c | ||
|
|
877c9c4ff9 | ||
|
|
ac0b7291dd | ||
|
|
491ef6085a | ||
|
|
3cedef4974 | ||
|
|
22ab04bd2b | ||
|
|
e5a080aebe | ||
|
|
c560cc5add | ||
|
|
0d27bc323b | ||
|
|
c71d3cef18 | ||
|
|
4e2d8daa3a | ||
|
|
d0f51da0af | ||
|
|
aeb73bca27 | ||
|
|
f239b03291 | ||
|
|
297c2965f4 | ||
|
|
daddf4d98e | ||
|
|
e05f9bfebf | ||
|
|
ef46aeae6f | ||
|
|
7edb3fd856 | ||
|
|
43b79778ea | ||
|
|
37f1f53b56 | ||
|
|
67bd1cdcd9 | ||
|
|
506385e5a2 | ||
|
|
2d4586b1c4 | ||
|
|
1476b9cb9c | ||
|
|
7bdde68ece | ||
|
|
50f92f55e2 | ||
|
|
370db62bf0 | ||
|
|
65f34f1d31 | ||
|
|
00a3908abb | ||
|
|
32034d590a | ||
|
|
3635fb3910 | ||
|
|
d2ecfe5491 | ||
|
|
1f759dce5b | ||
|
|
ae41c6038b | ||
|
|
f41f7eb2ad | ||
|
|
d04f383161 | ||
|
|
cb989d7489 | ||
|
|
b5cba19d26 | ||
|
|
b7271d7a46 | ||
|
|
768bdc2cc2 | ||
|
|
a0a093e260 | ||
|
|
0b2a7fdc15 | ||
|
|
f1876e20f8 | ||
|
|
7d6a6e53f7 | ||
|
|
6b765a1f77 | ||
|
|
3b3c93dd53 | ||
|
|
521083a309 | ||
|
|
6d35325601 | ||
|
|
3c65f37d84 | ||
|
|
d75dd058d6 | ||
|
|
40475e02ec | ||
|
|
f6ea9c1300 | ||
|
|
902359f24e | ||
|
|
bb4d5f1e93 | ||
|
|
1dec3f000e | ||
|
|
6d89814795 | ||
|
|
f339bc9f7f | ||
|
|
bb80bc0c07 | ||
|
|
96677ff8bf | ||
|
|
c7b6a6df79 | ||
|
|
8bb7e8bfb2 | ||
|
|
02ab343e22 | ||
|
|
4d875a18de | ||
|
|
cff2336923 | ||
|
|
0e96bfdfa3 | ||
|
|
a24ef48ad1 | ||
|
|
fe9c4f3391 | ||
|
|
6cbb0739f8 | ||
|
|
79ebabfcfb | ||
|
|
0c8e3262fe | ||
|
|
c24a040c19 | ||
|
|
4f39cb8dfa | ||
|
|
cdd2816642 | ||
|
|
27b6dfa4d2 | ||
|
|
204cbd0209 | ||
|
|
b7980aad5a | ||
|
|
e175729e2c | ||
|
|
c26ad4c807 | ||
|
|
4372adc703 | ||
|
|
8a0c2b9b13 | ||
|
|
4443a1c030 | ||
|
|
39a02235cf | ||
|
|
db89561480 | ||
|
|
a4cbb11693 | ||
|
|
1ceeacc526 | ||
|
|
b59052cc43 | ||
|
|
b5d8161bc2 | ||
|
|
b497eda26e | ||
|
|
44019e1042 | ||
|
|
fd0e1da4a2 | ||
|
|
f6c67b475a | ||
|
|
ea18a5ad15 | ||
|
|
4d0cd3f497 | ||
|
|
7dbdeaebe0 | ||
|
|
2085bcceb6 | ||
|
|
c09c962cb6 | ||
|
|
9108459ae4 | ||
|
|
992bac0b1f | ||
|
|
ebd6917a1d | ||
|
|
3e079e3b80 | ||
|
|
2ca20be197 | ||
|
|
17f23f3751 | ||
|
|
8e3d699856 | ||
|
|
f1a168fa53 | ||
|
|
3575113655 | ||
|
|
fe9dd7d23f | ||
|
|
9feb9d04b3 | ||
|
|
5419b1439a | ||
|
|
6f8fbe3f09 | ||
|
|
e4489343fe | ||
|
|
d9f4a5793d | ||
|
|
70fcdc9ebb | ||
|
|
78e7a81638 | ||
|
|
58e82d5dbd | ||
|
|
06d15be43a | ||
|
|
e1e7011853 | ||
|
|
eff7645035 | ||
|
|
eb75e52278 | ||
|
|
15e6148272 | ||
|
|
ccd448a675 | ||
|
|
db54c019ad | ||
|
|
b762b4d72c | ||
|
|
2f8faa839d | ||
|
|
831c325c63 | ||
|
|
f4f73078c5 | ||
|
|
f7d43ad5af | ||
|
|
a77c777980 | ||
|
|
a34db7449e | ||
|
|
0283bd2f97 | ||
|
|
a8de2f8ae7 | ||
|
|
d5dee75df3 | ||
|
|
6b7631ed5e | ||
|
|
79cb5c0631 | ||
|
|
7d9901540f | ||
|
|
1b56c0f191 | ||
|
|
94cbeba495 | ||
|
|
962f8233b0 | ||
|
|
31923d511e | ||
|
|
fdbb8300d3 | ||
|
|
203d8bca57 | ||
|
|
74c331eaf7 | ||
|
|
54365528a8 | ||
|
|
bc174f7054 |
@@ -0,0 +1 @@
|
||||
../.trae/skills
|
||||
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: greper
|
||||
buy_me_a_coffee: greper
|
||||
custom: ['https://afdian.com/a/greper']
|
||||
@@ -1,36 +1,29 @@
|
||||
---
|
||||
name: Plugin Apply
|
||||
about: 请求支持新部署插件
|
||||
title: "[Plugin] "
|
||||
about: 部署插件申请支持
|
||||
title: '[Plugin] '
|
||||
labels: feature
|
||||
---
|
||||
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# 新部署插件申请支持
|
||||
|
||||
## 1. 需求描述
|
||||
`请在此处简要描述你的需求`
|
||||
|
||||
`请在此处简要描述你的需求`
|
||||
|
||||
## 2. 要部署证书应用的信息
|
||||
|
||||
1. 应用名称:
|
||||
|
||||
|
||||
2. 应用网址/项目地址/官方网站:
|
||||
|
||||
|
||||
3. 管理证书界面截图(或者手动部署证书方式介绍及截图):
|
||||
|
||||
|
||||
4. 是否有API接口,接口地址:
|
||||
|
||||
|
||||
5. 如果没有API接口,网页登录是否需要验证码:
|
||||
|
||||
|
||||
6. 是否可以提供测试账号?(如果可以请留下联系方式或者加作者好友)
|
||||
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
---
|
||||
name: DNS Provider Apply
|
||||
about: 请求支持新的域名提供商
|
||||
title: "[DNS] "
|
||||
about: 域名提供商申请支持
|
||||
title: '[DNS] '
|
||||
labels: feature
|
||||
---
|
||||
|
||||
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# 新域名提供商支持申请
|
||||
|
||||
## 1. 基本信息
|
||||
|
||||
请填写如下内容:
|
||||
|
||||
1. 域名提供商名称:
|
||||
|
||||
1. 域名提供商名称:
|
||||
|
||||
2. 管理页面地址:
|
||||
|
||||
3. 是否有API接口,接口地址:
|
||||
|
||||
3. 是否有API接口,接口地址:
|
||||
|
||||
|
||||
4. 如果没有API接口,网页登录是否有验证码:
|
||||
|
||||
4. 如果没有API接口,网页登录是否有验证码:
|
||||
|
||||
5. 是否可以提供测试账号?(如果可以请留下联系方式或者加作者好友)
|
||||
|
||||
|
||||
|
||||
## 2. 截图
|
||||
## 2. 截图
|
||||
|
||||
`域名管理页面截图`
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: 报告一个错误或问题
|
||||
title: "[BUG] "
|
||||
about: 错误或问题报告
|
||||
title: '[BUG] '
|
||||
labels: bug
|
||||
---
|
||||
|
||||
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# bug提交
|
||||
|
||||
## 1、问题描述
|
||||
|
||||
`请在此处简要描述你所遇到的问题,必要时请贴出相关截图辅助理解和定位`
|
||||
|
||||
### 2、复现步骤
|
||||
|
||||
`请描述复现问题的详细步骤`
|
||||
`如果非示例页面的问题,最好能提供最小复现示例的代码、或者仓库链接`
|
||||
|
||||
|
||||
### 3.报错截图
|
||||
|
||||
`请贴出报错日志截图`
|
||||
|
||||
### 4、效果截图
|
||||
|
||||
`请贴出效果截图`
|
||||
|
||||
#### 4.1. 期望效果
|
||||
|
||||
#### 4.2. 实际效果
|
||||
@@ -1,23 +1,25 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: 新需求、新特性
|
||||
title: "[Feature] "
|
||||
about: 新需求、新特性申请支持
|
||||
title: '[Feature] '
|
||||
labels: feature
|
||||
---
|
||||
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# 新特性申请
|
||||
|
||||
# 新需求申请
|
||||
> 注意:这里仅供如果是要申请新的部署插件,请提交插件申请
|
||||
|
||||
## 1. 需求描述,需求背景
|
||||
|
||||
`请在此处简要描述你所遇到的问题,必要时请贴出相关截图辅助理解`
|
||||
|
||||
|
||||
## 2. 期望效果
|
||||
|
||||
`必要时可以截图描述你的期望效果`
|
||||
|
||||
|
||||
## 3. 你的解决方案
|
||||
|
||||
`如果你有解决方案,请描述你的方案`
|
||||
@@ -1,99 +0,0 @@
|
||||
name: build-image-for-test
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- "build-dev.trigger"
|
||||
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-certd-image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: v2-dev
|
||||
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pnpmWorkspace = "./pnpm-workspace.yaml";
|
||||
fs.unlinkSync(pnpmWorkspace)
|
||||
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
# - name: Use Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# cache: 'npm'
|
||||
# working-directory: ./packages/ui/certd-client
|
||||
- run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm run build
|
||||
working-directory: ./packages/ui/certd-client
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to aliyun container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-shenzhen.aliyuncs.com
|
||||
username: ${{ secrets.aliyun_cs_username }}
|
||||
password: ${{ secrets.aliyun_cs_password }}
|
||||
|
||||
- name: Login to GitHub Packages
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username }}
|
||||
password: ${{ secrets.dockerhub_password }}
|
||||
|
||||
# - name: Build default platforms
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
# push: true
|
||||
# context: ./packages/ui/
|
||||
# tags: |
|
||||
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-dev:latest
|
||||
# greper/certd-dev:latest
|
||||
# ghcr.io/${{ github.repository }}:dev-latest
|
||||
|
||||
- name: Build armv7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/arm/v7
|
||||
push: true
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
greper/certd:armv7
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
@@ -3,8 +3,8 @@ on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- "build.trigger"
|
||||
|
||||
- 'trigger/build.trigger'
|
||||
workflow_dispatch: # 添加手动触发
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
|
||||
ref: 'v2-dev'
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
@@ -37,14 +37,14 @@ jobs:
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
# - name: Use Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# cache: 'npm'
|
||||
# working-directory: ./packages/ui/certd-client
|
||||
# - name: Use Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# cache: 'npm'
|
||||
# working-directory: ./packages/ui/certd-client
|
||||
- run: |
|
||||
npm install -g pnpm
|
||||
npm install -g pnpm@10.33.4
|
||||
pnpm install
|
||||
npm run build
|
||||
working-directory: ./packages/ui/certd-client
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -80,36 +80,8 @@ jobs:
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
provenance: false
|
||||
sbom: false
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
|
||||
greper/certd:latest
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}
|
||||
- name: Build armv7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/arm/v7
|
||||
push: true
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
greper/certd:armv7
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
ghcr.io/${{ github.repository }}:armv7
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
|
||||
# - name: Build agent
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
# push: true
|
||||
# context: ./packages/ui/agent/
|
||||
# tags: |
|
||||
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:latest
|
||||
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||
# greper/certd-agent:latest
|
||||
# greper/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||
|
||||
@@ -3,11 +3,12 @@ on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- "deploy.trigger"
|
||||
- 'trigger/deploy.trigger'
|
||||
workflow_run:
|
||||
workflows: [ "build-image" ]
|
||||
workflows: ['build-image']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch: # 添加手动触发
|
||||
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
@@ -18,12 +19,14 @@ permissions:
|
||||
jobs:
|
||||
deploy-certd-demo:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: v2-dev
|
||||
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
@@ -41,9 +44,10 @@ jobs:
|
||||
with:
|
||||
time: '10' # for 60 seconds
|
||||
- name: deploy-certd-demo
|
||||
uses: tyrrrz/action-http-request@master
|
||||
uses: tyrrrz/action-http-request@prime
|
||||
with:
|
||||
url: http://flow-openapi.aliyun.com/pipeline/webhook/lzCzlGrLCOHQaTMMt0mG
|
||||
# 通过webhook 触发 certd-demo来部署
|
||||
url: ${{ secrets.WEBHOOK_CERTD_DEMO }}
|
||||
method: POST
|
||||
headers: |
|
||||
Content-Type: application/json
|
||||
@@ -53,15 +57,3 @@ jobs:
|
||||
}
|
||||
retry-count: 3
|
||||
retry-delay: 5000
|
||||
|
||||
- name: deploy-certd-doc
|
||||
uses: tyrrrz/action-http-request@master
|
||||
with:
|
||||
url: http://flow-openapi.aliyun.com/pipeline/webhook/IiSxLDp9aOhgDUxJPytv
|
||||
method: POST
|
||||
body: |
|
||||
{}
|
||||
headers: |
|
||||
Content-Type: application/json
|
||||
retry-count: 3
|
||||
retry-delay: 5000
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
name: publish-atomgit
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- 'trigger/publish.trigger'
|
||||
workflow_run:
|
||||
workflows: ['build-image-for-release']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch: # 添加手动触发
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish-atomgit:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.workflow_run.conclusion == 'success')
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: 'v2-dev'
|
||||
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pnpmWorkspace = "./pnpm-workspace.yaml";
|
||||
fs.unlinkSync(pnpmWorkspace)
|
||||
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
- run: |
|
||||
npm install -g pnpm@10.33.4
|
||||
pnpm install
|
||||
npm run build
|
||||
working-directory: ./packages/ui/certd-client
|
||||
|
||||
- name: publish_to_atomgit
|
||||
id: publish_to_atomgit
|
||||
run: |
|
||||
rootDir=$(pwd)
|
||||
rm -rf ./packages/ui/certd-client/dist/**/*.gz
|
||||
cd ./packages/ui/certd-client/dist && zip -r ../ui.zip .
|
||||
cd $rootDir
|
||||
export ATOMGIT_TOKEN=${{ secrets.ATOMGIT_TOKEN }}
|
||||
pnpm install
|
||||
npm run publish_to_atomgit
|
||||
working-directory: ./
|
||||
@@ -0,0 +1,41 @@
|
||||
name: publish-gitee
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- 'trigger/publish.trigger'
|
||||
workflow_run:
|
||||
workflows: ['build-image-for-release']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch: # 添加手动触发
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish-gitee:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.workflow_run.conclusion == 'success')
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: 'v2-dev'
|
||||
|
||||
- name: publish_to_gitee
|
||||
id: publish_to_gitee
|
||||
run: |
|
||||
export GITEE_TOKEN=${{ secrets.GITEE_TOKEN }}
|
||||
rm -rf ./pnpm*.yaml
|
||||
npm install -g pnpm@10.33.4
|
||||
pnpm install
|
||||
npm run publish_to_gitee
|
||||
working-directory: ./
|
||||
@@ -0,0 +1,41 @@
|
||||
name: publish-github
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- 'trigger/publish.trigger'
|
||||
workflow_run:
|
||||
workflows: ['build-image-for-release']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch: # 添加手动触发
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish-github:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.workflow_run.conclusion == 'success')
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: 'v2-dev'
|
||||
|
||||
- name: publish_to_github
|
||||
id: publish_to_github
|
||||
run: |
|
||||
export GITHUB_TOKEN=${{ secrets.GH_TOKEN }}
|
||||
rm -rf ./pnpm*.yaml
|
||||
npm install -g pnpm@10.33.4
|
||||
pnpm install
|
||||
npm run publish_to_github
|
||||
working-directory: ./
|
||||
@@ -0,0 +1,166 @@
|
||||
name: build-image-for-release
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- 'trigger/release.trigger'
|
||||
workflow_dispatch: # 添加手动触发
|
||||
# workflow_run:
|
||||
# workflows: [ "deploy-demo" ]
|
||||
# types:
|
||||
# - completed
|
||||
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-certd-image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: 'v2-dev'
|
||||
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pnpmWorkspace = "./pnpm-workspace.yaml";
|
||||
fs.unlinkSync(pnpmWorkspace)
|
||||
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
# - name: Use Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# cache: 'npm'
|
||||
# working-directory: ./packages/ui/certd-client
|
||||
- run: |
|
||||
npm install -g pnpm@10.33.4
|
||||
pnpm install
|
||||
npm run build
|
||||
working-directory: ./packages/ui/certd-client
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to aliyun container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-shenzhen.aliyuncs.com
|
||||
username: ${{ secrets.aliyun_cs_username }}
|
||||
password: ${{ secrets.aliyun_cs_password }}
|
||||
|
||||
- name: Login to GitHub Packages
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username }}
|
||||
password: ${{ secrets.dockerhub_password }}
|
||||
|
||||
- name: Build default platforms
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
provenance: false
|
||||
sbom: false
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}
|
||||
greper/certd:latest
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}
|
||||
- name: Build slim
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
provenance: false
|
||||
sbom: false
|
||||
context: ./packages/ui/
|
||||
build-args: |
|
||||
base_type=slim
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:slim
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
greper/certd:slim
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
ghcr.io/${{ github.repository }}:slim
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-slim
|
||||
|
||||
- name: Build armv7
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
platforms: linux/arm/v7
|
||||
push: true
|
||||
provenance: false
|
||||
sbom: false
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
greper/certd:armv7
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
ghcr.io/${{ github.repository }}:armv7
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
|
||||
- name: Login to Certd Docker Hub account
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username2 }}
|
||||
password: ${{ secrets.dockerhub_password2 }}
|
||||
|
||||
- name: Publish to certd Docker Hub repository
|
||||
run: |
|
||||
docker buildx imagetools create --tag certd/certd:latest greper/certd:latest
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }} greper/certd:${{ steps.get_certd_version.outputs.result }}
|
||||
docker buildx imagetools create --tag certd/certd:slim greper/certd:slim
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }}-slim greper/certd:${{ steps.get_certd_version.outputs.result }}-slim
|
||||
docker buildx imagetools create --tag certd/certd:armv7 greper/certd:armv7
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }}-armv7 greper/certd:${{ steps.get_certd_version.outputs.result }}-armv7
|
||||
|
||||
- name: deploy-certd-doc
|
||||
uses: tyrrrz/action-http-request@prime
|
||||
with:
|
||||
url: ${{ secrets.WEBHOOK_CERTD_DOC }}
|
||||
method: POST
|
||||
body: |
|
||||
{
|
||||
"CERTD_VERSION": "1.0.0"
|
||||
}
|
||||
headers: |
|
||||
Content-Type: application/json
|
||||
retry-count: 3
|
||||
retry-delay: 5000
|
||||
@@ -0,0 +1,84 @@
|
||||
name: stable-release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "版本号(如 v1.42.5)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
make-stable:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to aliyun container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.cn-shenzhen.aliyuncs.com
|
||||
username: ${{ secrets.aliyun_cs_username }}
|
||||
password: ${{ secrets.aliyun_cs_password }}
|
||||
|
||||
- name: Login to GitHub Packages
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username2 }}
|
||||
password: ${{ secrets.dockerhub_password2 }}
|
||||
|
||||
# stable 镜像:叠加 ENV 层
|
||||
- name: Build and push stable images
|
||||
run: |
|
||||
echo "FROM greper/certd:${{ inputs.version }}
|
||||
ENV certd_release_mode=stable" > Dockerfile.stable
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--push \
|
||||
-f Dockerfile.stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:stable \
|
||||
-t certd/certd:stable \
|
||||
-t ghcr.io/${{ github.repository }}:stable \
|
||||
.
|
||||
|
||||
# slim-stable 镜像
|
||||
- name: Build and push slim-stable images
|
||||
run: |
|
||||
echo "FROM greper/certd:${{ inputs.version }}-slim
|
||||
ENV certd_release_mode=stable" > Dockerfile.slim-stable
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--push \
|
||||
-f Dockerfile.slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:slim-stable \
|
||||
-t certd/certd:slim-stable \
|
||||
-t ghcr.io/${{ github.repository }}:slim-stable \
|
||||
.
|
||||
|
||||
- name: Set AtomGit release as stable
|
||||
run: |
|
||||
export ATOMGIT_TOKEN=${{ secrets.ATOMGIT_TOKEN }}
|
||||
export VERSION=${{ inputs.version }}
|
||||
node ./scripts/set-release-stable-fetch.js
|
||||
@@ -0,0 +1,34 @@
|
||||
name: sync-to-atomgit-dev
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://greper:${{secrets.ATOMGIT_TOKEN}}@atomgit.com
|
||||
|
||||
- name: push to atomgit # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://atomgit.com/certd/certd
|
||||
git push --set-upstream upstream v2-dev
|
||||
@@ -0,0 +1,34 @@
|
||||
name: sync-to-atomgit
|
||||
on:
|
||||
push:
|
||||
branches: ['v2']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://greper:${{secrets.ATOMGIT_TOKEN}}@atomgit.com
|
||||
|
||||
- name: push to atomgit # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://atomgit.com/certd/certd
|
||||
git push --set-upstream upstream v2
|
||||
@@ -0,0 +1,34 @@
|
||||
name: sync-to-cnb-dev
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||
|
||||
- name: push to cnb # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://cnb.cool/certd/certd.git
|
||||
git push --set-upstream upstream v2-dev
|
||||
@@ -0,0 +1,33 @@
|
||||
name: sync-to-cnb
|
||||
on:
|
||||
push:
|
||||
branches: ['v2']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||
|
||||
- name: push to cnb # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://cnb.cool/certd/certd.git
|
||||
git push --set-upstream upstream v2
|
||||
@@ -12,23 +12,23 @@ jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://${{secrets.PUSH_TOKEN_GITEE}}@gitee.com
|
||||
|
||||
- name: push to gitee # 4. 执行同步
|
||||
- name: push to gitee # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://gitee.com/certd/certd
|
||||
git push --set-upstream upstream v2-dev
|
||||
|
||||
|
||||
@@ -12,23 +12,22 @@ jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://${{secrets.PUSH_TOKEN_GITEE}}@gitee.com
|
||||
|
||||
- name: push to gitee # 4. 执行同步
|
||||
- name: push to gitee # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://gitee.com/certd/certd
|
||||
git push --set-upstream upstream v2
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
./packages/core/lego
|
||||
./packages/core/lego
|
||||
# IntelliJ project files
|
||||
.vscode/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
@@ -30,5 +29,17 @@ test/**/*.js
|
||||
/packages/ui/certd-server/data/db.sqlite
|
||||
/packages/ui/certd-server/data/keys.yaml
|
||||
/packages/pro/
|
||||
test.js
|
||||
.history
|
||||
/logs
|
||||
.pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
.studio/
|
||||
|
||||
test.js
|
||||
# Certd 推广报告,仅本地使用
|
||||
/popularize/reports/
|
||||
output/
|
||||
.uploads/
|
||||
.certd-plugin-history/
|
||||
.tmp
|
||||
.plugin-dev/
|
||||
@@ -1,2 +1,6 @@
|
||||
link-workspace-packages=deep
|
||||
prefer-workspace-packages=true
|
||||
better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
better-sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
better-sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
@@ -0,0 +1,419 @@
|
||||
---
|
||||
name: access-plugin-dev
|
||||
description: 用于开发 Certd 系统中的 Access 插件,存储用户第三方应用授权数据并对接实现第三方 API 接口。当用户需要创建授权插件、实现第三方API接口、添加新的授权方式或修改现有 Access 插件时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Access 插件开发技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 插件开发专家,擅长创建和实现 Access 类型的插件,熟悉 TypeScript 编程和 Certd 插件开发规范。
|
||||
|
||||
## 核心指令
|
||||
|
||||
请严格按照以下步骤执行任务:
|
||||
|
||||
1. **导入必要的依赖**
|
||||
|
||||
- 导入 `AccessInput`, `BaseAccess`, `IsAccess`, `Pager`, `PageRes`, `PageSearch` 等必要的类型和装饰器
|
||||
- 导入 `DomainRecord` 等相关类型
|
||||
|
||||
2. **使用 @IsAccess 注解注册插件**
|
||||
|
||||
- 配置插件的唯一标识、标题、图标和描述
|
||||
- 继承 `BaseAccess` 类
|
||||
|
||||
3. **定义授权属性**
|
||||
|
||||
- 使用 `@AccessInput` 注解定义授权属性
|
||||
- 配置属性的标题、默认值、组件类型和验证规则
|
||||
- 对于敏感信息,设置 `encrypt: true` 进行加密
|
||||
|
||||
4. **实现测试方法**
|
||||
|
||||
- 添加测试按钮配置
|
||||
- 实现 `onTestRequest` 方法,用于测试接口调用是否正常
|
||||
|
||||
5. **实现 API 方法**
|
||||
|
||||
- 实现必要的 API 方法,如 `GetDomainList`
|
||||
- 封装统一的 API 请求方法 `doRequest`,处理错误和日志
|
||||
|
||||
6. **遵循开发最佳实践**
|
||||
- 使用 `this.ctx.logger` 输出日志
|
||||
- 统一处理错误,抛出明确的错误信息
|
||||
- 实现代码复用,避免重复逻辑
|
||||
|
||||
## 输出规范
|
||||
|
||||
- 必须包含完整的插件实现代码,包括所有必要的导入语句
|
||||
- 代码必须包含详细的注释说明,解释每个步骤的目的和实现细节
|
||||
- 提供完整的示例代码,展示插件的使用方法,包括不同类型的授权方式
|
||||
- 代码块必须使用正确的语法高亮,确保代码可读性
|
||||
- 包含开发技巧和注意事项,帮助开发者避免常见错误
|
||||
- 输出内容必须结构清晰,使用适当的标题和列表格式
|
||||
|
||||
## 示例
|
||||
|
||||
### 示例 1: 基本 Access 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个 Access 插件,用于存储第三方应用的授权信息并提供 API 接口。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline';
|
||||
import { DomainRecord } from '@certd/plugin-lib';
|
||||
|
||||
/**
|
||||
* 这个注解将注册一个授权配置
|
||||
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||
*/
|
||||
@IsAccess({
|
||||
name: 'demo',
|
||||
title: '授权插件示例',
|
||||
icon: 'clarity:plugin-line', //插件图标
|
||||
desc: '这是一个示例授权插件,用于演示如何实现一个授权插件',
|
||||
})
|
||||
export class DemoAccess extends BaseAccess {
|
||||
/**
|
||||
* 授权属性配置
|
||||
*/
|
||||
@AccessInput({
|
||||
title: '授权方式',
|
||||
value: 'apiKey', //默认值
|
||||
component: {
|
||||
name: 'a-select', //基于antdv的输入组件
|
||||
vModel: 'value', // v-model绑定的属性名
|
||||
options: [
|
||||
//组件参数
|
||||
{
|
||||
label: 'API密钥(推荐)',
|
||||
value: 'apiKey',
|
||||
},
|
||||
{
|
||||
label: '账号密码',
|
||||
value: 'account',
|
||||
},
|
||||
],
|
||||
placeholder: 'demoKeyId',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
apiType = '';
|
||||
|
||||
/**
|
||||
* 授权属性配置
|
||||
*/
|
||||
@AccessInput({
|
||||
title: '密钥Id',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
allowClear: true,
|
||||
placeholder: 'demoKeyId',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
demoKeyId = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '密钥', //标题
|
||||
required: true, //text组件可以省略
|
||||
encrypt: true, //该属性是否需要加密
|
||||
})
|
||||
demoKeySecret = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '测试',
|
||||
component: {
|
||||
name: 'api-test',
|
||||
action: 'TestRequest',
|
||||
},
|
||||
helper: '点击测试接口是否正常',
|
||||
})
|
||||
testRequest = true;
|
||||
|
||||
/**
|
||||
* 会通过上面的testRequest参数在ui界面上生成测试按钮,供用户测试接口调用是否正常
|
||||
*/
|
||||
async onTestRequest() {
|
||||
await this.GetDomainList({});
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* api接口示例 获取域名列表
|
||||
*/
|
||||
async GetDomainList(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
//输出日志必须使用ctx.logger
|
||||
this.ctx.logger.info(`获取域名列表,req:${JSON.stringify(req)}`);
|
||||
const pager = new Pager(req);
|
||||
const resp = await this.doRequest({
|
||||
action: 'ListDomains',
|
||||
data: {
|
||||
domain: req.searchKey,
|
||||
offset: pager.getOffset(),
|
||||
limit: pager.pageSize,
|
||||
},
|
||||
});
|
||||
const total = resp?.TotalCount || 0;
|
||||
let list = resp?.DomainList?.map(item => {
|
||||
item.domain = item.Domain;
|
||||
item.id = item.DomainId;
|
||||
return item;
|
||||
});
|
||||
return {
|
||||
total,
|
||||
list,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用api调用方法, 具体如何构造请求体,需参考对应应用的API文档
|
||||
*/
|
||||
async doRequest(req: { action: string; data?: any }) {
|
||||
const res = await this.ctx.http.request({
|
||||
url: 'https://api.demo.cn/api/',
|
||||
method: 'POST',
|
||||
data: {
|
||||
Action: req.action,
|
||||
Body: req.data,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.Code !== 0) {
|
||||
//异常处理
|
||||
throw new Error(res.Message || '请求失败');
|
||||
}
|
||||
return res.Resp;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例 2: 支持 OAuth 授权的 Access 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个支持 OAuth 授权方式的 Access 插件。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline';
|
||||
import { DomainRecord } from '@certd/plugin-lib';
|
||||
|
||||
/**
|
||||
* OAuth 授权插件示例
|
||||
*/
|
||||
@IsAccess({
|
||||
name: 'oauth-demo',
|
||||
title: 'OAuth授权插件示例',
|
||||
icon: 'clarity:plugin-line',
|
||||
desc: '这是一个支持OAuth授权的插件示例',
|
||||
})
|
||||
export class OAuthDemoAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
title: '授权方式',
|
||||
value: 'oauth',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
options: [
|
||||
{
|
||||
label: 'OAuth授权',
|
||||
value: 'oauth',
|
||||
},
|
||||
{
|
||||
label: 'API密钥',
|
||||
value: 'apiKey',
|
||||
},
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
authType = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '客户端ID',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: 'Client ID',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
clientId = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '客户端密钥',
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
clientSecret = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '授权回调地址',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: 'https://your-domain.com/callback',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
redirectUri = '';
|
||||
|
||||
@AccessInput({
|
||||
title: 'AccessToken',
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
accessToken = '';
|
||||
|
||||
@AccessInput({
|
||||
title: 'RefreshToken',
|
||||
encrypt: true,
|
||||
})
|
||||
refreshToken = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '测试',
|
||||
component: {
|
||||
name: 'api-test',
|
||||
action: 'TestOAuth',
|
||||
},
|
||||
helper: '点击测试OAuth授权是否正常',
|
||||
})
|
||||
testOAuth = true;
|
||||
|
||||
/**
|
||||
* 测试OAuth授权
|
||||
*/
|
||||
async onTestOAuth() {
|
||||
try {
|
||||
// 测试AccessToken是否有效
|
||||
const result = await this.doOAuthRequest('GET', '/api/user/profile');
|
||||
this.ctx.logger.info('OAuth测试成功:', result);
|
||||
return 'OAuth授权测试成功';
|
||||
} catch (error) {
|
||||
this.ctx.logger.error('OAuth测试失败:', error);
|
||||
throw new Error('OAuth授权测试失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth API请求方法
|
||||
*/
|
||||
async doOAuthRequest(method: string, endpoint: string, data?: any) {
|
||||
const res = await this.ctx.http.request({
|
||||
url: `https://api.oauth-demo.com${endpoint}`,
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data,
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`API请求失败: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新AccessToken
|
||||
*/
|
||||
async refreshAccessToken() {
|
||||
if (!this.refreshToken) {
|
||||
throw new Error('没有提供RefreshToken');
|
||||
}
|
||||
|
||||
const res = await this.ctx.http.request({
|
||||
url: 'https://api.oauth-demo.com/oauth/token',
|
||||
method: 'POST',
|
||||
data: {
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: this.refreshToken,
|
||||
client_id: this.clientId,
|
||||
client_secret: this.clientSecret,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status === 200 && res.data.access_token) {
|
||||
this.accessToken = res.data.access_token;
|
||||
if (res.data.refresh_token) {
|
||||
this.refreshToken = res.data.refresh_token;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
throw new Error('刷新AccessToken失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名列表
|
||||
*/
|
||||
async GetDomainList(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
try {
|
||||
const res = await this.doOAuthRequest('GET', '/api/domains', {
|
||||
search: req.searchKey,
|
||||
page: req.page,
|
||||
pageSize: req.pageSize,
|
||||
});
|
||||
|
||||
return {
|
||||
total: res.total,
|
||||
list: res.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
domain: item.domain,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
// 尝试刷新AccessToken并重试
|
||||
if (error.message.includes('401')) {
|
||||
await this.refreshAccessToken();
|
||||
const res = await this.doOAuthRequest('GET', '/api/domains', {
|
||||
search: req.searchKey,
|
||||
page: req.page,
|
||||
pageSize: req.pageSize,
|
||||
});
|
||||
|
||||
return {
|
||||
total: res.total,
|
||||
list: res.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
domain: item.domain,
|
||||
})),
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
2. **属性加密**:对于敏感信息(如密钥),应设置 `encrypt: true`。
|
||||
3. **日志输出**:必须使用 `this.ctx.logger` 输出日志,而不是 `console`,参数文本化,不要传对象,否则会输出`[object Object]}`。
|
||||
4. **错误处理**:API 调用失败时应抛出明确的错误信息。
|
||||
5. **测试方法**:实现 `onTestRequest` 方法,以便用户可以测试授权是否正常。
|
||||
6. **统一接口调用**:封装统一的 API 请求方法,避免重复编写错误处理逻辑。
|
||||
7. **旧版数据兼容**: 新增注解的插件参数,必须要考虑旧版数据兼容,比如新增一个deployType参数,有两种值:`default`和`custom`,需要在使用时判空,走旧版逻辑。
|
||||
|
||||
## 开发技巧
|
||||
|
||||
### 实现统一的 API 请求封装
|
||||
|
||||
**好处:**
|
||||
|
||||
- **代码复用**:避免在每个 API 方法中重复编写相同的 header 设置和错误处理逻辑
|
||||
- **错误处理一致**:统一捕获和处理各种错误情况,确保错误信息格式统一
|
||||
- **日志记录完善**:集中记录详细的错误信息,便于调试和问题排查
|
||||
- **接口调用简化**:调用方只需关注业务逻辑,无需关心底层请求细节
|
||||
- **易于维护**:统一修改 API 调用方式时,只需修改一处代码
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
我需要开发一个 Access 插件,用于存储和管理第三方应用的授权信息。请指导我如何实现。
|
||||
@@ -0,0 +1,145 @@
|
||||
# Access 插件开发指南
|
||||
|
||||
## 开发步骤
|
||||
|
||||
### 1. 导入必要的依赖
|
||||
|
||||
```typescript
|
||||
import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline';
|
||||
import { DomainRecord } from '@certd/plugin-lib';
|
||||
```
|
||||
|
||||
### 2. 使用 @IsAccess 注解注册插件
|
||||
|
||||
```typescript
|
||||
@IsAccess({
|
||||
name: 'demo', // 插件唯一标识
|
||||
title: '授权插件示例', // 插件标题
|
||||
icon: 'clarity:plugin-line', // 插件图标
|
||||
desc: '这是一个示例授权插件,用于演示如何实现一个授权插件', // 插件描述
|
||||
})
|
||||
export class DemoAccess extends BaseAccess {
|
||||
// 插件实现...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 定义授权属性
|
||||
|
||||
使用 `@AccessInput` 注解定义授权属性:
|
||||
|
||||
```typescript
|
||||
@AccessInput({
|
||||
title: '授权方式',
|
||||
value: 'apiKey', // 默认值
|
||||
component: {
|
||||
name: "a-select", // 基于 antdv 的输入组件
|
||||
vModel: "value", // v-model 绑定的属性名
|
||||
options: [ // 组件参数
|
||||
{ label: "API密钥(推荐)", value: "apiKey" },
|
||||
{ label: "账号密码", value: "account" },
|
||||
],
|
||||
placeholder: 'demoKeyId',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
apiType = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '密钥Id',
|
||||
component: {
|
||||
name:"a-input",
|
||||
allowClear: true,
|
||||
placeholder: 'demoKeyId',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
demoKeyId = '';
|
||||
|
||||
@AccessInput({
|
||||
title: '密钥',//标题
|
||||
required: true, //text组件可以省略
|
||||
encrypt: true, //该属性是否需要加密
|
||||
})
|
||||
demoKeySecret = '';
|
||||
```
|
||||
|
||||
### 4. 实现测试方法
|
||||
|
||||
```typescript
|
||||
@AccessInput({
|
||||
title: "测试",
|
||||
component: {
|
||||
name: "api-test",
|
||||
action: "TestRequest"
|
||||
},
|
||||
helper: "点击测试接口是否正常"
|
||||
})
|
||||
testRequest = true;
|
||||
|
||||
/**
|
||||
* 会通过上面的testRequest参数在ui界面上生成测试按钮,供用户测试接口调用是否正常
|
||||
*/
|
||||
async onTestRequest() {
|
||||
await this.GetDomainList({});
|
||||
return "ok"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 实现 API 方法
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 获api接口示例 取域名列表,
|
||||
*/
|
||||
async GetDomainList(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
//输出日志必须使用ctx.logger
|
||||
this.ctx.logger.info(`获取域名列表,req:${JSON.stringify(req)}`);
|
||||
const pager = new Pager(req);
|
||||
const resp = await this.doRequest({
|
||||
action: "ListDomains",
|
||||
data: {
|
||||
domain: req.searchKey,
|
||||
offset: pager.getOffset(),
|
||||
limit: pager.pageSize,
|
||||
}
|
||||
});
|
||||
const total = resp?.TotalCount || 0;
|
||||
let list = resp?.DomainList?.map((item) => {
|
||||
item.domain = item.Domain;
|
||||
item.id = item.DomainId;
|
||||
return item;
|
||||
})
|
||||
return {
|
||||
total,
|
||||
list
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用api调用方法, 具体如何构造请求体,需参考对应应用的API文档
|
||||
*/
|
||||
async doRequest(req: { action: string, data?: any }) {
|
||||
const res = await this.ctx.http.request({
|
||||
url: "https://api.demo.cn/api/",
|
||||
method: "POST",
|
||||
data: {
|
||||
Action: req.action,
|
||||
Body: req.data
|
||||
}
|
||||
});
|
||||
|
||||
if (res.Code !== 0) {
|
||||
//异常处理
|
||||
throw new Error(res.Message || "请求失败");
|
||||
}
|
||||
return res.Resp;
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
2. **属性加密**:对于敏感信息(如密钥),应设置 `encrypt: true`。
|
||||
3. **日志输出**:必须使用 `this.ctx.logger` 输出日志,而不是 `console`。
|
||||
4. **错误处理**:API 调用失败时应抛出明确的错误信息。
|
||||
5. **测试方法**:实现 `onTestRequest` 方法,以便用户可以测试授权是否正常。
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: certd-online-plugin-dev
|
||||
description: 用于通过 Certd API 开发、修改、读取、保存和恢复在线插件。当用户要求使用 Codex 或 Trae 创建 Task、DNS Provider、Access 插件,或优化已有 Certd 插件时使用。
|
||||
---
|
||||
|
||||
# Certd 在线插件开发 Skill
|
||||
|
||||
## 开发方式
|
||||
|
||||
本 Skill 使用 Certd HTTP API,不使用 WebSocket,也不依赖浏览器传递代码草稿。
|
||||
|
||||
- 前端只生成包含需求、API 地址和认证 Token 的启动提示词。
|
||||
- Agent 直接调用 Certd API 读取和保存插件 YAML。
|
||||
- Agent 的进度、日志和代码修改在 Codex/Trae 中查看。
|
||||
- 插件开发临时文件和修改历史统一保存在 Agent 工作区的 `.plugin-dev/` 下,不保存到浏览器或 Certd 后端。
|
||||
|
||||
## 启动检查
|
||||
|
||||
开始开发前先确认当前工作目录是否已经在 Certd 项目内:
|
||||
|
||||
- 应存在 `package.json`。
|
||||
- 应存在 `packages/ui/certd-server/src/plugins/`。
|
||||
- 应存在 `.trae/skills/`。
|
||||
- 应存在 `.trae/skills/certd-online-plugin-dev/SKILL.md`。
|
||||
|
||||
如果当前目录不是 Certd 项目,或缺少 `certd-online-plugin-dev` Skill,先拉取 Certd 仓库代码并切换到仓库内工作( --depth 1 拉取第一层即可):
|
||||
|
||||
1. 优先使用 `https://atomgit.com/certd/certd/`。
|
||||
2. 如果 AtomGit 拉取失败,再使用 `https://github.com/certd/certd`。
|
||||
3. 拉取后重新检查上述特征,并读取 `.trae/skills/certd-online-plugin-dev/SKILL.md`。
|
||||
|
||||
开发插件时,参考 Certd 项目下已有内置插件实现:`packages/ui/certd-server/src/plugins/`。
|
||||
|
||||
## 插件来源
|
||||
|
||||
Certd 插件按来源分为三类:
|
||||
|
||||
- 内置插件:`type: "builtIn"`,随 Certd 安装包提供。可读取并在流水线中使用,不应通过本 Skill 修改或覆盖。
|
||||
- 市场插件:`type: "store"`,且存在 `appId` 或 `developerId`。它来自在线插件市场,可能尚未安装到本地;是否可修改只能以接口返回的 `editable` 为准。
|
||||
- 本地插件:`type: "store"`,但没有 `appId` 和 `developerId`。它是当前 Certd 实例本地创建、导入或复制的插件,可直接保存;发布到市场后会带上市场归属信息。
|
||||
|
||||
不要只根据 `type: "store"` 判断插件是否来自市场,也不要自行推断编辑权限;始终使用列表结果中的 `editable` 字段。
|
||||
|
||||
## API 认证
|
||||
|
||||
提示词会提供 Certd API 地址和仅限 AI 插件开发接口的受限 Token。调用 API 时使用:
|
||||
|
||||
```http
|
||||
Authorization: <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
不要把 Token 写入代码、历史摘要、日志、提交信息或插件 YAML。
|
||||
|
||||
所有 Certd API 请求统一使用 Node.js 的 `fetch`。不要使用 PowerShell 的 `Invoke-RestMethod`、`Invoke-WebRequest` 或 .NET HTTP 客户端发送插件 YAML/JSON;它们在 Windows 上可能造成中文乱码或使完整 YAML 导入请求长时间无响应。
|
||||
|
||||
Node 请求须直接读取 UTF-8 文件或在 Node 内构造 JSON,并使用 `JSON.stringify`:
|
||||
|
||||
```javascript
|
||||
const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: token, "Content-Type": "application/json; charset=utf-8" },
|
||||
body: JSON.stringify({ keywords: ["nginx"], includeBuiltIn: true, includeStore: true }),
|
||||
})
|
||||
```
|
||||
|
||||
## UTF-8 保存
|
||||
|
||||
在 Windows 上,Node 直接以 UTF-8 读取 YAML 并用 `JSON.stringify` 发送;不要让 PowerShell 转发含中文的 YAML/JSON。保存后检查中文字段不含 `?`,再调用 `/scoped/sys/ai/plugin/find` 或 `/scoped/sys/ai/plugin/info` 验证。
|
||||
|
||||
## API 工作流
|
||||
|
||||
1. 使用 `/scoped/sys/ai/plugin/find` 查询插件和 Access,可通过 `keywords` 数组传递多个关键词。
|
||||
2. 查询结果包含 `editable`:
|
||||
- `editable: true`:允许当前 Agent 修改并保存。
|
||||
- `editable: false`:只能读取和使用,不能修改。
|
||||
3. 读取完整 YAML 时调用 `/scoped/sys/ai/plugin/export`。
|
||||
4. 所有插件保存统一调用 `/scoped/sys/ai/plugin/import`,并始终传递完整 YAML:
|
||||
- 新插件使用 `override: false`。
|
||||
- 已有插件使用 `override: true`;导入接口根据 `author` 和 `name` 定位并覆盖已有记录。
|
||||
5. 不使用 `/sys/plugin/add` 或 `/sys/plugin/update` 保存插件,避免保存路径分叉、字段丢失和 Windows 请求兼容性问题。
|
||||
6. 保存完成后重新调用 `/scoped/sys/ai/plugin/find` 或 `/scoped/sys/ai/plugin/info` 验证结果。
|
||||
|
||||
`/scoped/sys/ai/plugin/find` 会在一次请求中分别查询内置插件和 `store` 插件,再合并返回;`store` 插件需按上述字段区分市场插件与本地插件。
|
||||
|
||||
详细请求字段见 `references/certd-api.md`。
|
||||
|
||||
## Access 协作
|
||||
|
||||
开发 Task 或 DNS Provider 前,先用 `/scoped/sys/ai/plugin/find` 查询对应 Access:
|
||||
|
||||
1. 如果没有对应 Access,先创建 Access 插件,再创建业务插件。
|
||||
2. 如果已有 Access,先读取它的完整 YAML 和 `content`。
|
||||
3. 如果 Access 已提供所需 API/SDK,业务插件优先复用。
|
||||
4. 如果缺少能力:
|
||||
- `editable: true`:优先修改 Access,并先保存历史。
|
||||
- `editable: false`:在当前业务插件中实现必要的 API 调用。
|
||||
5. 业务插件通过 `dependPlugins` 声明 Access 依赖。
|
||||
|
||||
详细规则见 `references/access-development.md`。
|
||||
|
||||
## 本地历史
|
||||
|
||||
开发插件时,必须在当前工作区创建并使用 `.plugin-dev/{plugin-name}` 作为插件开发目录。历史记录、临时 YAML、脚本草稿和调试记录都放在该目录下。
|
||||
|
||||
每次修改插件前,必须将完整 YAML 保存到 `.plugin-dev/{plugin-name}/history/`:
|
||||
|
||||
```text
|
||||
.plugin-dev/{plugin-name}/xxxxx.yaml
|
||||
.plugin-dev/{plugin-name}/history/
|
||||
2026-08-02T12-30-00-before-edit.yaml
|
||||
2026-08-02T12-30-00-change.md
|
||||
```
|
||||
|
||||
保存要求:
|
||||
|
||||
- 修改前保存完整 YAML。
|
||||
- 修改后保存修改摘要。
|
||||
- 恢复前再次备份当前版本。
|
||||
- 不上传历史文件,不保存 Token、证书、私钥或真实授权值。
|
||||
|
||||
详细格式见 `references/local-history.md`。
|
||||
|
||||
## YAML 和脚本规范
|
||||
|
||||
插件始终以完整 YAML 传递和保存,脚本源码放在顶层 `content` 字段。
|
||||
|
||||
- 统一使用 `await _ctx.import(...)` 引用模块。
|
||||
- `"/@/..."` 表示以绝对路径引用 `server/src/` 下的模块。
|
||||
- 最后返回继承目标基类的 class。
|
||||
- 不使用 `import`、`export`、装饰器或独立源码文件语法。
|
||||
- 使用 `this.logger` 打印插件执行日志。
|
||||
- 使用 `this.ctx.http` 访问 HTTP 能力。
|
||||
- 失败时抛出 `Error`。
|
||||
|
||||
需要字段格式时读取 `references/online-yaml-format.md`。
|
||||
需要组件示例时读取 `references/component-examples.md`。
|
||||
|
||||
## 示例插件
|
||||
|
||||
开发对应类型插件前,先读取 `examples/` 下的示例:
|
||||
|
||||
- Access:`examples/DemoAccess.yaml`
|
||||
- 部署/Task:`examples/DemoDeploy.yaml`
|
||||
- DNS Provider:`examples/DemoDnsProvider.yaml`
|
||||
|
||||
示例是完整在线插件 YAML,重点参考 `input` 配置、依赖声明和 `content` 脚本结构。
|
||||
|
||||
## 子 Skill
|
||||
|
||||
- Task:`skills/task-plugin-dev/SKILL.md`
|
||||
- DNS Provider:`skills/dns-provider-dev/SKILL.md`
|
||||
- Access:`skills/access-plugin-dev/SKILL.md`
|
||||
|
||||
## 安全边界
|
||||
|
||||
Certd 会保存证书、私钥、API Token、云厂商密钥、SSH 凭据和其他敏感授权。
|
||||
|
||||
- 禁止读取、打印或上传真实授权值。
|
||||
- 禁止把认证 Token 写入插件、历史、日志或摘要。
|
||||
- 禁止读取无关的证书、私钥、Cookie、环境变量和系统设置。
|
||||
- 只使用脱敏示例数据和公开文档。
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Certd 在线插件开发"
|
||||
short_description: "通过 Certd API 使用 Codex 或 Trae 开发在线插件"
|
||||
default_prompt: "读取在线插件 YAML 和对应类型规范,按需求修改 content,保存历史记录后通过 Certd API 写回。"
|
||||
@@ -0,0 +1,37 @@
|
||||
name: DemoAccess
|
||||
icon: logos:airflow-icon
|
||||
title: Demo-授权插件示例 # 模块-插件名
|
||||
group: null
|
||||
desc: 这只是一个示例
|
||||
version: 1.0.0
|
||||
pluginType: access
|
||||
author: greper
|
||||
input:
|
||||
username:
|
||||
title: 用户名
|
||||
required: true
|
||||
encrypt: false
|
||||
component:
|
||||
name: a-input
|
||||
allowClear: true
|
||||
password:
|
||||
title: 密码
|
||||
required: true
|
||||
encrypt: true
|
||||
component:
|
||||
name: a-input
|
||||
allowClear: true
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
content: |
|
||||
|
||||
// 必须使用 await import 来引入模块
|
||||
const { BaseAccess } = await import("@certd/pipeline")
|
||||
// 需要返回一个继承BaseAccess的类
|
||||
return class DemoAccess extends BaseAccess {
|
||||
// 授权的字段,跟左边input一一对应
|
||||
username;
|
||||
password;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
name: DemoDeploy
|
||||
icon: logos:amp-icon
|
||||
title: Demo-部署插件示例 # 模块-插件名
|
||||
group: cdn
|
||||
desc: 这仅仅是一个示例
|
||||
version: 1.0.0
|
||||
pluginType: deploy
|
||||
author: greper
|
||||
input:
|
||||
cert:
|
||||
title: 前置任务证书
|
||||
helper: 请选择前置任务产生的证书
|
||||
component:
|
||||
name: output-selector # 前置任务输出选择组件
|
||||
vModel: modelValue
|
||||
from:
|
||||
- ':cert:' # 前置任务输出作为输入的筛选条件 , 格式 pluginType:valueType:keyName , 为空表示任意匹配
|
||||
required: true
|
||||
certDomains:
|
||||
title: 当前证书域名
|
||||
component:
|
||||
name: cert-domains-getter
|
||||
mergeScript: |
|
||||
return {
|
||||
component:{
|
||||
inputKey: ctx.compute(({form})=>{
|
||||
return form.cert
|
||||
}),
|
||||
}
|
||||
}
|
||||
required: true
|
||||
accessId:
|
||||
title: Access授权
|
||||
helper: xxxx的授权
|
||||
component:
|
||||
name: access-selector
|
||||
type: aliyun
|
||||
required: true
|
||||
key1:
|
||||
title: 输入示例1
|
||||
required: false
|
||||
key2:
|
||||
title: 可选项
|
||||
component:
|
||||
name: a-select
|
||||
vMode: value
|
||||
options:
|
||||
- value: '1'
|
||||
label: 选项1
|
||||
- value: '2'
|
||||
label: 选项2
|
||||
required: false
|
||||
showRunStrategy: false # 用户是否可以选择运行策略, 【默认不显示】
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 0 # 运行策略, 0 = 成功后跳过,1 = 正常运行
|
||||
content: >
|
||||
|
||||
// 要用await来import模块
|
||||
|
||||
const { AbstractTaskPlugin } = await _ctx.import("@certd/pipeline")
|
||||
|
||||
// 使用_ctx.import("/@/xxx.js") 以绝对路径引用模块,/@相当于根路径
|
||||
|
||||
const {AliyunAccess} = await _ctx.import("/@/plugins/plugin-lib/aliyun/access/index.js")
|
||||
|
||||
_ctx.logger.info("AliyunAccess:",AliyunAccess)
|
||||
|
||||
// 要返回一个继承AbstractTaskPlugin的class
|
||||
|
||||
return class DemoTask extends AbstractTaskPlugin {
|
||||
// 这里是插件的输入参数,对应左边的input配置
|
||||
cert;
|
||||
certDomains;
|
||||
accessId;
|
||||
key1;
|
||||
key2;
|
||||
// 编写执行方法
|
||||
async execute(){
|
||||
// 根据accessId获取授权配置
|
||||
const access = await this.getAccess(this.accessId)
|
||||
|
||||
//必须使用this.logger打印日志
|
||||
// this.logger.info("cert:",this.cert);
|
||||
this.logger.info("certDomains:",this.certDomains);
|
||||
this.logger.info("access:",access);
|
||||
this.logger.info("key1:",this.key1);
|
||||
this.logger.info("key2:",this.key2);
|
||||
this.logger.info("开始xxx部署任务")
|
||||
// 你的部署任务代码 【必须实现】
|
||||
// this.ctx里面有一些常用的方法类,比如utils、http、logger等
|
||||
const res = await this.ctx.http.request({url:"https://www.baidu.com"})
|
||||
if(res.error){
|
||||
//抛出异常,终止任务,否则将被判定为执行成功
|
||||
throw new Error("部署失败:"+res.message)
|
||||
}
|
||||
this.logger.info("执行成功")
|
||||
// this.outputName = xxxx //设置输出参数,可以被其他插件选择使用
|
||||
|
||||
// 此处可以返回 "skip" , 表示本次任务没有做任何操作和改变 , 配合runStrategy=1使用
|
||||
// return "skip"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
name: DemoDnsProvider
|
||||
icon: fa-solid:frog
|
||||
title: Demo-Dns提供商插件示例 # 模块-插件名
|
||||
desc: 这只是一个示例
|
||||
type: custom
|
||||
version: 1.0.0
|
||||
pluginType: dnsProvider
|
||||
author: greper
|
||||
accessType: aliyun # 需要的授权类型
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
content: |+
|
||||
|
||||
const { AbstractDnsProvider } = await _ctx.import("@certd/pipeline")
|
||||
return class DemoDnsProvider extends AbstractDnsProvider {
|
||||
// 创建dns解析记录,用于验证域名所有权 【必须实现】
|
||||
async createRecord(options) {
|
||||
/**
|
||||
* fullRecord: '_acme-challenge.test.example.com',
|
||||
* value: 一串uuid
|
||||
* type: 'TXT',
|
||||
* domain: 'example.com'
|
||||
*/
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
const access = this.ctx.access
|
||||
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
|
||||
// const record = await sdk.createRecord() // 调用对应的接口创建解析记录
|
||||
|
||||
//返回解析记录,用于后面清理
|
||||
return record
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除dns解析记录,清理申请痕迹【必须实现】
|
||||
* @param options
|
||||
*/
|
||||
async removeRecord(options) {
|
||||
const { fullRecord, value } = options.recordReq;
|
||||
const record = options.recordRes; // createRecord接口返回的record
|
||||
const access = this.ctx.access
|
||||
this.logger.info('删除域名解析:', fullRecord, value);
|
||||
if (!record) {
|
||||
this.logger.info('record为空,不执行删除');
|
||||
return;
|
||||
}
|
||||
const recordId = record.id;
|
||||
// 这里调用删除txt dns解析记录接口
|
||||
// sdk.removeRecord(recordId)
|
||||
this.logger.info("删除域名解析成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名列表 【可选,没有实现的话,不支持在certd域名管理中导入域名列表,不影响证书申请】
|
||||
* @param req
|
||||
* @returns
|
||||
*/
|
||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
const pager = new Pager(req);
|
||||
const params = {
|
||||
RegionId: "cn-hangzhou",
|
||||
PageSize: pager.pageSize,
|
||||
PageNumber: pager.pageNo,
|
||||
};
|
||||
|
||||
const requestOption = {
|
||||
method: "POST",
|
||||
};
|
||||
|
||||
const ret = await this.client.request("DescribeDomains", params, requestOption);
|
||||
const list =
|
||||
ret.Domains?.Domain?.map(item => ({
|
||||
id: item.DomainId,
|
||||
domain: item.DomainName,
|
||||
})) || [];
|
||||
|
||||
return {
|
||||
list,
|
||||
total: ret.TotalCount,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名解析记录列表 【可选,没有实现的话,不支持在站点监控里面导入网址,不影响证书申请】
|
||||
* @param domain
|
||||
* @param req
|
||||
* @returns
|
||||
*/
|
||||
async getRecordListPage(domain: string, req: PageSearch): Promise<PageRes<DnsResolveRecord>> {
|
||||
const pager = new Pager(req);
|
||||
const params = {
|
||||
RegionId: "cn-hangzhou",
|
||||
DomainName: domain,
|
||||
PageSize: pager.pageSize,
|
||||
PageNumber: pager.pageNo,
|
||||
};
|
||||
|
||||
const requestOption = {
|
||||
method: "POST",
|
||||
};
|
||||
|
||||
const ret = await this.client.request("DescribeDomainRecords", params, requestOption);
|
||||
const rawList = ret.DomainRecords?.Record || [];
|
||||
const list = rawList.map(item => ({
|
||||
id: item.RecordId,
|
||||
hostRecord: item.RR,
|
||||
fullRecord: item.RR === "@" ? domain : `${item.RR}.${domain}`,
|
||||
type: item.Type,
|
||||
value: item.Value,
|
||||
}));
|
||||
|
||||
return {
|
||||
list,
|
||||
total: ret.TotalCount,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Access 开发规范
|
||||
|
||||
Access 插件负责保存授权配置,也负责封装平台 API/SDK,供 Task 和 DNS Provider 复用。
|
||||
|
||||
## 查询顺序
|
||||
|
||||
1. 调用 `/scoped/sys/ai/plugin/find`,使用 `pluginType: access`。
|
||||
2. 根据 `name`、`author`、`fullName` 识别目标 Access。
|
||||
3. 使用 `/scoped/sys/ai/plugin/export` 读取完整 YAML。
|
||||
4. 检查 `content` 中已经提供的方法。
|
||||
|
||||
## 修改规则
|
||||
|
||||
- Access 的 `editable: true` 时才允许修改。
|
||||
- 修改前先保存本地历史。
|
||||
- 优先把通用 API/SDK 能力放入 Access。
|
||||
- 业务插件通过 `dependPlugins` 依赖 Access。
|
||||
- `editable: false` 时不要尝试修改 Access,在业务插件内部实现必要的调用。
|
||||
- 不要在日志中打印完整授权配置。
|
||||
@@ -0,0 +1,70 @@
|
||||
# Certd API
|
||||
|
||||
以下接口都以前端生成提示词中的 API 地址为基础地址,并使用 `Authorization` 请求头。
|
||||
|
||||
## 查询插件
|
||||
|
||||
```http
|
||||
POST /scoped/sys/ai/plugin/find
|
||||
Content-Type: application/json
|
||||
Authorization: <token>
|
||||
```
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"keywords": ["aliyun", "dns"],
|
||||
"pluginType": "access",
|
||||
"includeBuiltIn": true,
|
||||
"includeStore": true
|
||||
}
|
||||
```
|
||||
|
||||
接口会分别查询内置插件和 `store` 插件,再合并返回:
|
||||
|
||||
- `type: "builtIn"`:内置插件,不通过在线开发 API 修改。
|
||||
- `type: "store"` 且有 `appId` 或 `developerId`:市场插件。
|
||||
- `type: "store"` 且没有 `appId`、`developerId`:本地插件。
|
||||
|
||||
结果中的 `editable` 是唯一的编辑权限依据;不能只按插件来源判断是否可修改。
|
||||
列表结果只返回插件基础信息,不返回 `content`、`setting`、`sysSetting`、`metadata` 或 `extra`。需要完整 YAML 时再调用 `/scoped/sys/ai/plugin/export`。
|
||||
|
||||
## 读取插件信息
|
||||
|
||||
```http
|
||||
POST /scoped/sys/ai/plugin/info?id=12
|
||||
Authorization: <token>
|
||||
```
|
||||
|
||||
## 导出完整 YAML
|
||||
|
||||
```http
|
||||
POST /scoped/sys/ai/plugin/export
|
||||
Content-Type: application/json
|
||||
Authorization: <token>
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 12
|
||||
}
|
||||
```
|
||||
|
||||
## 保存插件
|
||||
|
||||
使用完整 YAML 导入:
|
||||
|
||||
```http
|
||||
POST /scoped/sys/ai/plugin/import
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "完整 YAML",
|
||||
"override": true,
|
||||
"type": "store"
|
||||
}
|
||||
```
|
||||
|
||||
保存后重新调用 `/scoped/sys/ai/plugin/find` 或 `/scoped/sys/ai/plugin/info` 验证。
|
||||
@@ -0,0 +1,423 @@
|
||||
# Component Examples
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins`.
|
||||
Detected **30** distinct component names.
|
||||
|
||||
These snippets are extracted from existing Certd plugins. For online plugins, place the object under `input.<field>.component` in the YAML document.
|
||||
|
||||
## `EmailSelector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-other/plugins/plugin-deploy-to-mail.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "EmailSelector",
|
||||
vModel: "value",
|
||||
mode: "tags",
|
||||
}
|
||||
```
|
||||
|
||||
## `ParamsShow`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-template/email/plugin-common.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "ParamsShow",
|
||||
params: [
|
||||
{ label: "标题", value: "title" },
|
||||
{ label: "内容", value: "content" },
|
||||
{ label: "URL", value: "url" },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## `RemoteSelect`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/getter/aliyun.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "RemoteSelect",
|
||||
vModel: "value",
|
||||
pager: true,
|
||||
single: true,
|
||||
}
|
||||
```
|
||||
|
||||
## `a-alert`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-template/email/plugin-base.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-alert",
|
||||
props: {
|
||||
type: "info",
|
||||
message: "在标题和内容模版中,通过${name}引用参数,例如: 感谢注册,您的注册验证码为:${code}",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `a-auto-complete`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-auto-complete",
|
||||
vModel: "value",
|
||||
options: [
|
||||
{ value: "cn-qingdao", label: "华北1(青岛)" },
|
||||
{ value: "cn-beijing", label: "华北2(北京)" },
|
||||
{ value: "cn-zhangjiakou", label: "华北3(张家口)" },
|
||||
{ value: "cn-huhehaote", label: "华北5(呼和浩特)" },
|
||||
{ value: "cn-wulanchabu", label: "华北6(乌兰察布)" },
|
||||
{ value: "cn-hangzhou", label: "华东1(杭州)" },
|
||||
{ value: "cn-shanghai", label: "华东2(上海)" },
|
||||
{ value: "cn-shenzhen", label: "华南1(深圳)" },
|
||||
{ value: "cn-guangzhou", label: "华南3(广州)" },
|
||||
{ value: "ap-southeast-2", label: "澳大利亚(悉尼)" },
|
||||
{ value: "ap-southeast-3", label: "马来西亚(吉隆坡)" },
|
||||
{ value: "ap-northeast-1", label: "日本(东京)" },
|
||||
{ value: "cn-chengdu", label: "西南1(成都)" },
|
||||
{ value: "ap-southeast-1", label: "新加坡" },
|
||||
{ value: "ap-southeast-5", label: "印度尼西亚(雅加达)" },
|
||||
{ value: "cn-hongkong", label: "中国香港" },
|
||||
{ value: "eu-central-1", label: "德国(法兰克福)" },
|
||||
{ value: "us-east-1", label: "美国(弗吉尼亚)" },
|
||||
{ value: "us-west-1", label: "美国(硅谷)" },
|
||||
{ value: "eu-west-1", label: "英国(伦敦)" },
|
||||
{ value: "me-east-1", label: "阿联酋(迪拜)" },
|
||||
//金融云
|
||||
{ value: "cn-beijing-finance-1", label: "华北2 金融云(邀测)" },
|
||||
{ value: "cn-hangzhou-finance", label: "华东1 金融云" },
|
||||
{ value: "cn-shanghai-finance-1", label: "华东2 金融云" },
|
||||
{ value: "cn-shenzhen-finance-1", label: "华南1 金融云" },
|
||||
],
|
||||
placeholder: "集群所属大区",
|
||||
}
|
||||
```
|
||||
|
||||
## `a-input`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-admin/plugin-db-backup.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-input",
|
||||
type: "value",
|
||||
placeholder: `默认${defaultBackupDir}`,
|
||||
}
|
||||
```
|
||||
|
||||
## `a-input-number`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-acepanel/access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-input-number",
|
||||
vModel: "value",
|
||||
}
|
||||
```
|
||||
|
||||
## `a-input-password`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-51dns/access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-input-password",
|
||||
vModel: "value",
|
||||
placeholder: "密码",
|
||||
}
|
||||
```
|
||||
|
||||
## `a-radio-group`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-esa/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-radio-group",
|
||||
vModel: "value",
|
||||
options: [
|
||||
{ label: "边缘证书", value: "edge" },
|
||||
{ label: "SaaS证书", value: "saas" },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## `a-select`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-admin/plugin-db-backup.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-select",
|
||||
options: [
|
||||
{ label: "本地复制", value: "local" },
|
||||
{ label: "oss上传(推荐)", value: "oss" },
|
||||
{ label: "ssh上传(请使用oss上传方式)", value: "ssh", disabled: true },
|
||||
],
|
||||
placeholder: "",
|
||||
}
|
||||
```
|
||||
|
||||
## `a-switch`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-acepanel/access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked",
|
||||
}
|
||||
```
|
||||
|
||||
## `a-textarea`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-admin/plugin-script.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "a-textarea",
|
||||
vModel: "value",
|
||||
rows: 10,
|
||||
style: "background-color: #000c17;color: #fafafa;",
|
||||
}
|
||||
```
|
||||
|
||||
## `access-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-acepanel/plugins/plugin-deploy-to-website.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "access-selector",
|
||||
type: "acepanel",
|
||||
}
|
||||
```
|
||||
|
||||
## `api-test`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-51dns/access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "api-test",
|
||||
action: "TestRequest",
|
||||
}
|
||||
```
|
||||
|
||||
## `cert-info-updater`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/custom/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "cert-info-updater",
|
||||
vModel: "modelValue",
|
||||
}
|
||||
```
|
||||
|
||||
## `dns-provider-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/apply.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "dns-provider-selector",
|
||||
}
|
||||
```
|
||||
|
||||
## `domain-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/base-convert.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "domain-selector",
|
||||
vModel: "value",
|
||||
mode: "tags",
|
||||
// open: false,
|
||||
placeholder: "请输入证书域名/IP,比如:foo.com , *.foo.com , *.sub.foo.com , *.bar.com , 123.123.123.123",
|
||||
tokenSeparators: [",", " ", ",", "、", "|"],
|
||||
search: true,
|
||||
pager: true,
|
||||
}
|
||||
```
|
||||
|
||||
## `domains-verify-plan-editor`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/apply.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "domains-verify-plan-editor",
|
||||
}
|
||||
```
|
||||
|
||||
## `email-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/base.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "email-selector",
|
||||
vModel: "value",
|
||||
}
|
||||
```
|
||||
|
||||
## `fs-icon-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-oauth/oidc/plugin-oidc.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "fs-icon-selector",
|
||||
vModel: "modelValue",
|
||||
iconSets: IconSets,
|
||||
}
|
||||
```
|
||||
|
||||
## `icon-select`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/apply.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "icon-select",
|
||||
vModel: "value",
|
||||
options: [
|
||||
{ value: "letsencrypt", label: "Let's Encrypt(免费,新手推荐,支持IP证书)", icon: "simple-icons:letsencrypt" },
|
||||
{ value: "google", label: "Google(免费)", icon: "flat-color-icons:google" },
|
||||
{ value: "zerossl", label: "ZeroSSL(免费)", icon: "emojione:digit-zero" },
|
||||
{ value: "litessl", label: "litessl(免费)", icon: "roentgen:free" },
|
||||
{ value: "sslcom", label: "SSL.com(仅主域名和www免费)", icon: "la:expeditedssl" },
|
||||
{ value: "letsencrypt_staging", label: "Let's Encrypt测试环境(仅供测试)", icon: "simple-icons:letsencrypt" },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## `input-password`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/base-convert.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "input-password",
|
||||
vModel: "value",
|
||||
}
|
||||
```
|
||||
|
||||
## `notification-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-github/plugins/plugin-check-release.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "notification-selector",
|
||||
select: {
|
||||
mode: "tags",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `output-selector`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-acepanel/plugins/plugin-deploy-to-website.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "output-selector",
|
||||
from: [...CertApplyPluginNames],
|
||||
}
|
||||
```
|
||||
|
||||
## `pem-input`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/custom/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN CERTIFICATE-----\n...\n...\n-----END CERTIFICATE-----",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## `refresh-input`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-cert/access/acme-account-access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "refresh-input",
|
||||
action: "GenerateAccount",
|
||||
buttonText: "生成ACME账号",
|
||||
successMessage: "ACME账号已生成,请保存授权配置",
|
||||
type: "textarea",
|
||||
rows: 4,
|
||||
}
|
||||
```
|
||||
|
||||
## `remote-auto-complete`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apig/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "remote-auto-complete",
|
||||
}
|
||||
```
|
||||
|
||||
## `remote-select`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-nginx-proxy-manager/plugins/plugin-deploy-to-proxy-hosts.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "remote-select",
|
||||
vModel: "value",
|
||||
mode: "tags",
|
||||
type: "plugin",
|
||||
action: "onGetProxyHostOptions",
|
||||
search: true,
|
||||
pager: false,
|
||||
single: false,
|
||||
watches: ["certDomains", "accessId"],
|
||||
}
|
||||
```
|
||||
|
||||
## `remote-tree-select`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-tencent/plugin/refresh-cert/index.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
name: "remote-tree-select",
|
||||
vModel: "value",
|
||||
action: TencentRefreshCert.prototype.onGetRegionsTree.name,
|
||||
pager: false,
|
||||
search: false,
|
||||
watches: ["certList"],
|
||||
}
|
||||
```
|
||||
|
||||
## `synology-device-id-getter`
|
||||
|
||||
Source: `packages/ui/certd-server/src/plugins/plugin-plus/synology/access.ts`
|
||||
|
||||
```ts
|
||||
component: {
|
||||
placeholder: "设备ID",
|
||||
name: "synology-device-id-getter",
|
||||
type: "access",
|
||||
typeName: "synology",
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
# 本地历史记录
|
||||
|
||||
历史记录和开发临时文件只保存在 Codex/Trae 当前工作区的 `.tmp/online-plugin-dev/` 下,不调用 Certd 后端历史接口。
|
||||
|
||||
目录格式:
|
||||
|
||||
```text
|
||||
.tmp/online-plugin-dev/
|
||||
history/
|
||||
plugin-12/
|
||||
2026-08-02T12-30-00-before-edit.yaml
|
||||
2026-08-02T12-30-00-change.md
|
||||
work/
|
||||
plugin-12.yaml
|
||||
plugin-12-content.js
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 修改前保存完整 YAML。
|
||||
- 临时 YAML、脚本草稿、调试记录都放到 `.tmp/online-plugin-dev/` 下,不散落到项目目录。
|
||||
- `change.md` 只记录插件 ID、版本、时间和脱敏修改摘要。
|
||||
- 不保存 Token、证书、私钥、Cookie、环境变量和真实授权值。
|
||||
- 恢复历史版本前先备份当前 YAML。
|
||||
- 恢复后通过 `/scoped/sys/ai/plugin/import` 写回 Certd。
|
||||
@@ -0,0 +1,66 @@
|
||||
## 在线插件 YAML
|
||||
|
||||
在线插件始终以一个完整 YAML 文档传递、编辑、导入和导出。脚本源码必须放在顶层 `content` 字段中,不要输出独立的 `.ts` 文件,也不要使用 JSON Patch。
|
||||
|
||||
常用字段:
|
||||
|
||||
```yaml
|
||||
name: DemoTask
|
||||
author: demo
|
||||
title: Demo 任务
|
||||
desc: 插件说明
|
||||
icon: clarity:plugin-line
|
||||
pluginType: task
|
||||
group: other
|
||||
version: 1.0.0
|
||||
input:
|
||||
cert: # 输入字段定义
|
||||
title: 域名证书
|
||||
required: true # 是否必填
|
||||
component: # 输入字段组件
|
||||
name: cert-select # 前端vue组件名称
|
||||
output: # 【可选】输出字段定义
|
||||
cert:
|
||||
title: 证书 #输出字段标题
|
||||
type: "cert" # 输出字段类型
|
||||
dependPlugins: # 依赖的插件
|
||||
# - access:certd/DemoAccess: *
|
||||
dependPackages: # 依赖的nodejs包
|
||||
# xxx: ^1.0.0
|
||||
vip: "free" # 默认免费,plus 需要专业版
|
||||
showRunStrategy: false # 【默认不显示】
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: 0, # 运行策略, 0 = 成功后跳过,1 = 正常运行
|
||||
},
|
||||
}
|
||||
content: |
|
||||
const { AbstractTaskPlugin } = await _ctx.import("@certd/pipeline")
|
||||
const { DemoAccess } = await _ctx.import("/@/plugins/plugin-lib/demo/access/index.js")
|
||||
_ctx.logger.info("DemoAccess:", DemoAccess)
|
||||
return class DemoTask extends AbstractTaskPlugin {
|
||||
async execute() {
|
||||
this.logger.info("执行成功")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `content` 规则
|
||||
|
||||
- 统一使用 `await _ctx.import(...)` 加载模块。
|
||||
- 使用 `_ctx.import("/@/...")` 以绝对路径加载 `certd-server/src/` 下的模块,`/@` 代表 `certd-server/src` 根路径。
|
||||
- 需要确认模块时使用 `_ctx.logger`,插件执行过程使用 `this.logger`。
|
||||
- 最后返回继承目标基类的 class。
|
||||
- 不使用 `import`、`export`、装饰器或独立源码文件语法。
|
||||
- 输入字段在 class 中声明为同名属性,并与 YAML 的 `input` 配置保持一致。
|
||||
- HTTP 使用 `this.ctx.http`;
|
||||
- 不要使用 `console.log`。
|
||||
- 读取授权使用 `await this.getAccess(accessId)` 或目标基类规定的授权方式。
|
||||
- 失败时抛出 `Error`,不要吞掉错误。
|
||||
|
||||
### 编辑规则
|
||||
|
||||
- 修改已有插件时保留 `name`、`author`、`pluginType` 和已有兼容字段。
|
||||
- 只修改需求涉及的字段,避免删除未知的 YAML 字段。
|
||||
- 脚本过长时仍放在同一个 `content` block scalar 中。
|
||||
- 提交前检查 YAML 可解析、`content` 非空、版本和插件类型没有被意外修改。
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
name: certd-online-access-plugin-dev
|
||||
description: 用于开发 Certd 在线 Access 插件。输出完整 YAML,content 中返回继承 BaseAccess 的 class,并在 input 中声明授权字段。
|
||||
---
|
||||
|
||||
# 在线 Access 插件
|
||||
|
||||
读取父 Skill 的 `references/online-yaml-format.md`。不要沿用代码版 `@IsAccess`、`@AccessInput` 装饰器和独立 TypeScript 文件。
|
||||
|
||||
## 输出结构
|
||||
|
||||
- `pluginType` 使用 `access`。
|
||||
- `input` 中声明用户需要填写的授权字段。
|
||||
- 敏感字段在 input 中设置加密或密码类组件。
|
||||
- `content` 中实现授权 class 和 API 方法。
|
||||
|
||||
## `content` 模板
|
||||
|
||||
```javascript
|
||||
const { BaseAccess } = await _ctx.import("@certd/pipeline")
|
||||
|
||||
return class DemoAccess extends BaseAccess {
|
||||
demoKeyId
|
||||
demoKeySecret
|
||||
|
||||
async onTestRequest() {
|
||||
await this.getDomainList({ searchKey: "" })
|
||||
return "ok"
|
||||
}
|
||||
|
||||
async getDomainList(req) {
|
||||
this.logger.info("获取域名列表", { searchKey: req.searchKey })
|
||||
const res = await this.ctx.http.request({
|
||||
url: "https://api.example.com/domains",
|
||||
method: "GET",
|
||||
params: { keyword: req.searchKey },
|
||||
})
|
||||
if (res.error) {
|
||||
throw new Error(`获取域名列表失败: ${res.message}`)
|
||||
}
|
||||
return {
|
||||
total: res.data?.total || 0,
|
||||
list: res.data?.list || [],
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编写要求
|
||||
|
||||
- 统一使用 `await _ctx.import(...)` 加载模块。
|
||||
- 使用 `_ctx.import("/@/...")` 通过绝对路径加载 `server/src/` 下的模块,`/@` 代表 `server/src` 根路径。
|
||||
- 需要记录模块加载信息时使用 `_ctx.logger`,访问执行日志使用 `this.logger`。
|
||||
- 返回继承 `BaseAccess` 的 class,不使用装饰器。
|
||||
- class 属性名必须与 YAML `input` 字段一致。
|
||||
- 所有敏感授权值只通过 `this` 和 Certd 授权上下文使用,不打印真实值。
|
||||
- `onTestRequest` 应调用实际 API 方法并在失败时抛出异常。
|
||||
- 对外 API 方法应统一处理分页、错误和返回字段。
|
||||
- 使用 `this.logger` 或框架提供的 logger,禁止 `console.log`。
|
||||
- 一般将API接口方法封装到Access中,其他plugin调用access的方法当做client sdk使用
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: certd-online-dns-provider-dev
|
||||
description: 用于开发 Certd 在线 DNS Provider 插件。输出完整 YAML,content 中返回继承 AbstractDnsProvider 的 class。
|
||||
---
|
||||
|
||||
# 在线 DNS Provider 插件
|
||||
|
||||
读取父 Skill 的 `references/online-yaml-format.md`。不要沿用旧版 `@IsDnsProvider` 装饰器和独立 TypeScript 文件。
|
||||
|
||||
## 输出结构
|
||||
|
||||
- `pluginType` 使用 `dnsProvider`。
|
||||
- `input` 中配置授权选择、域名或平台所需的参数。
|
||||
- `content` 中实现创建和删除 DNS 记录的 class。
|
||||
|
||||
## `content` 模板
|
||||
|
||||
```javascript
|
||||
const { AbstractDnsProvider } = await _ctx.import("@certd/pipeline")
|
||||
const { DemoAccess } = await _ctx.import("/@/plugins/plugin-lib/demo/access/index.js")
|
||||
_ctx.logger.info("DemoAccess:", DemoAccess)
|
||||
|
||||
return class DemoDnsProvider extends AbstractDnsProvider {
|
||||
accessId
|
||||
|
||||
async onInstance() {
|
||||
this.access = await this.getAccess(this.accessId)
|
||||
}
|
||||
|
||||
async createRecord(options) {
|
||||
const { fullRecord, value, type, domain } = options
|
||||
this.logger.info("添加 DNS 记录", { fullRecord, type, domain })
|
||||
const res = await this.ctx.http.request({
|
||||
url: "https://api.example.com/dns/records",
|
||||
method: "POST",
|
||||
data: { fullRecord, value, type, domain },
|
||||
})
|
||||
if (res.error) {
|
||||
throw new Error(`创建 DNS 记录失败: ${res.message}`)
|
||||
}
|
||||
return res.data
|
||||
}
|
||||
|
||||
async removeRecord(options) {
|
||||
const { fullRecord, value, domain } = options.recordReq
|
||||
const res = await this.ctx.http.request({
|
||||
url: "https://api.example.com/dns/records",
|
||||
method: "DELETE",
|
||||
data: { fullRecord, value, domain },
|
||||
})
|
||||
if (res.error) {
|
||||
this.logger.warn("删除 DNS 记录失败", res.message)
|
||||
return
|
||||
}
|
||||
this.logger.info("删除 DNS 记录成功", fullRecord)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编写要求
|
||||
|
||||
- 统一使用 `await _ctx.import(...)` 加载模块。
|
||||
- 使用 `_ctx.import("/@/...")` 通过绝对路径加载 `server/src/` 下的模块,`/@` 代表 `server/src` 根路径。
|
||||
- 需要记录模块加载信息时使用 `_ctx.logger`。
|
||||
- 返回继承 `AbstractDnsProvider` 的 class。
|
||||
- `createRecord` 必须返回删除时需要的记录信息。
|
||||
- `removeRecord` 使用 `options.recordReq` 和 `options.recordRes`。
|
||||
- 只处理业务 API 所需的 TXT 记录参数,不在日志中输出授权密钥。
|
||||
- 网络失败、授权失败和 API 业务失败要有明确日志;创建失败必须抛出异常。
|
||||
- 保持创建和删除幂等,避免清理失败阻断无关流程。
|
||||
- 一般将API接口方法封装到Access中,其他plugin使用access当做client sdk使用
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: certd-online-task-plugin-dev
|
||||
description: 用于开发 Certd 在线 Task 插件。输出完整 YAML,脚本源码放在 content 字段中,继承 AbstractTaskPlugin 并返回插件 class。
|
||||
---
|
||||
|
||||
# 在线 Task 插件
|
||||
|
||||
读取父 Skill 的 `references/online-yaml-format.md`。在线插件不是原来的装饰器源码文件模式。
|
||||
|
||||
## 输出结构
|
||||
|
||||
- `pluginType` 使用 `task`。
|
||||
- 保留或填写 `name`、`author`、`title`、`desc`、`icon`、`group`、`version`。
|
||||
- 输入配置放在 YAML 的 `input` 字段。
|
||||
- 执行脚本放在 YAML 顶层 `content` 字段。
|
||||
|
||||
|
||||
## runStrategy
|
||||
|
||||
整个流水线每次运行,会记录每个任务的运行结果状态,如果上次任务执行成功,且 runStrategy 为成功后跳过,那么下次运行时会跳过该任务。
|
||||
|
||||
|
||||
- `showRunStrategy`: false 【默认不显示】
|
||||
- 任务配置时是否显示 runStrategy 字段,用于指定任务的执行策略。
|
||||
- `default.strategy.runStrategy`: 0 【默认成功后跳过】
|
||||
- 0 表示成功后跳过,1 表示正常运行。
|
||||
- 设置任务的默认运行策略runStrategy。
|
||||
|
||||
当证书申请任务,申请证书成功后,会清空后续任务状态。此时后续`runStrategy = 0` 的任务,会重新执行。
|
||||
这样可以保证相同的证书不会重复部署。
|
||||
|
||||
在其他场景的部署任务,可能希望每次运行都检查是否需要部署,如果即将过期就部署,没有过期就跳过。
|
||||
此时可以设置 `default.strategy.runStrategy = 1`,然后在`execute`方法中,如果没有过期,就返回 `"skip"`。
|
||||
|
||||
## `content` 模板
|
||||
|
||||
```javascript
|
||||
const { AbstractTaskPlugin } = await _ctx.import("@certd/pipeline")
|
||||
const { DemoAccess } = await _ctx.import("/@/plugins/plugin-lib/demo/access/index.js")
|
||||
_ctx.logger.info("DemoAccess:", DemoAccess)
|
||||
|
||||
return class DemoTask extends AbstractTaskPlugin {
|
||||
cert
|
||||
certDomains
|
||||
accessId
|
||||
|
||||
async execute() {
|
||||
const access = await this.getAccess(this.accessId)
|
||||
this.logger.info("开始执行任务", { access })
|
||||
const res = await this.ctx.http.request({
|
||||
url: "https://api.example.com",
|
||||
})
|
||||
if (res.error) {
|
||||
// 抛异常让任务执行失败
|
||||
throw new Error(`任务执行失败: ${res.message}`)
|
||||
}
|
||||
this.logger.info("执行成功")
|
||||
// 还可以返回 "skip" 表示当前任务没有执行任何操作
|
||||
// 一般与 runStrategy = 1 配合使用。 (1=正常运行,0表示成功后跳过)
|
||||
// 比如检查无需更新,时就 返回 "skip"
|
||||
// return "skip"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编写要求
|
||||
|
||||
- 统一用 `await _ctx.import(...)` 加载模块。
|
||||
- 使用 `_ctx.import("/@/...")` 通过绝对路径加载 `server/src/` 下的模块,`/@` 代表 `server/src` 根路径。
|
||||
- 需要记录模块加载信息时使用 `_ctx.logger`。
|
||||
- 返回继承 `AbstractTaskPlugin` 的 class,不写 `export class`。
|
||||
- class 属性名必须对应 `input` 配置的字段名。
|
||||
- 用 `this.logger` 记录关键步骤。
|
||||
- 用 `this.ctx.http` 请求远程 API,用 `this.getAccess` 获取授权。
|
||||
- 外部 API 返回失败或业务失败时抛出异常。
|
||||
- 对重复执行保持幂等,避免把真实 Token、证书和私钥写入日志。
|
||||
- 修改完成后把整个 YAML 通过 Certd `/scoped/sys/ai/plugin/import` 保存。
|
||||
- 一般将API接口方法封装到Access中,其他plugin使用access当做client sdk使用
|
||||
@@ -0,0 +1,457 @@
|
||||
---
|
||||
name: dns-provider-dev
|
||||
description: 用于开发 Certd 系统中的 DNS Provider 插件,在 ACME 申请证书时给域名添加 TXT 解析记录以验证域名所有权。当用户需要创建DNS提供商插件、实现DNS解析、ACME证书验证或修改现有 DNS Provider 插件时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# DNS Provider 插件开发技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 插件开发专家,擅长创建和实现 DNS Provider 类型的插件,熟悉 TypeScript 编程和 Certd 插件开发规范。
|
||||
|
||||
## 核心指令
|
||||
|
||||
请严格按照以下步骤执行任务:
|
||||
|
||||
1. **导入必要的依赖**
|
||||
|
||||
- 导入 `AbstractDnsProvider`, `CreateRecordOptions`, `IsDnsProvider`, `RemoveRecordOptions` 等必要的类型和装饰器
|
||||
- 导入对应的 Access 插件类型
|
||||
|
||||
2. **定义记录数据结构**
|
||||
|
||||
- 定义适合对应云平台的记录数据结构
|
||||
- 至少包含 id 字段,用于后续删除记录
|
||||
|
||||
3. **使用 @IsDnsProvider 注解注册插件**
|
||||
|
||||
- 配置插件的唯一标识、标题、描述、图标
|
||||
- 指定对应的云平台的 access 类型名称
|
||||
- 设置排序值(可选)
|
||||
- 继承 `AbstractDnsProvider` 类
|
||||
|
||||
4. **实现 onInstance 方法**
|
||||
|
||||
- 获取并保存对应的 Access 实例
|
||||
- 执行初始化操作
|
||||
|
||||
5. **实现 createRecord 方法**
|
||||
|
||||
- 解析传入的参数(fullRecord, value, type, domain)
|
||||
- 记录操作开始日志
|
||||
- 调用云平台 API 创建 TXT 类型的 DNS 解析记录
|
||||
- 处理可能的错误:网络错误、API调用失败、授权失败等
|
||||
- 记录操作结果日志
|
||||
- 返回创建的记录信息,用于后续删除操作
|
||||
|
||||
6. **实现 removeRecord 方法**
|
||||
|
||||
- 解析传入的参数和之前创建的记录信息
|
||||
- 记录操作开始日志
|
||||
- 调用云平台 API 删除 TXT 类型的 DNS 解析记录
|
||||
- 处理可能的错误:网络错误、API调用失败、记录不存在等
|
||||
- 记录操作结果日志
|
||||
|
||||
7. **实现 getDomainListPage 方法**
|
||||
|
||||
- 实现获取域名列表的方法
|
||||
- 支持分页查询
|
||||
- 处理可能的错误:网络错误、API调用失败、授权失败等
|
||||
- 返回标准化的域名列表格式
|
||||
|
||||
8. **实例化插件**
|
||||
- 实例化插件,确保插件被注册
|
||||
|
||||
## 输出规范
|
||||
|
||||
- 必须包含完整的插件实现代码
|
||||
- 代码必须包含详细的注释说明
|
||||
- 提供完整的示例代码,展示插件的使用方法
|
||||
- 包含开发注意事项
|
||||
|
||||
## 示例
|
||||
|
||||
### 示例 1: 基本 DNS Provider 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个 DNS Provider 插件,用于在 ACME 申请证书时添加和删除 TXT 解析记录。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { Pager, PageRes, PageSearch } from '@certd/pipeline';
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions, DomainRecord } from '@certd/plugin-cert';
|
||||
import { DemoAccess } from './access.js';
|
||||
|
||||
type DemoRecord = {
|
||||
// 这里定义 Record 记录的数据结构,跟对应云平台接口返回值一样即可
|
||||
id: string;
|
||||
};
|
||||
|
||||
// 这里通过 IsDnsProvider 注册一个 dnsProvider
|
||||
@IsDnsProvider({
|
||||
name: 'demo',
|
||||
title: 'Dns提供商Demo',
|
||||
desc: 'dns provider示例',
|
||||
icon: 'clarity:plugin-line',
|
||||
// 这里是对应的云平台的 access 类型名称
|
||||
accessType: 'demo',
|
||||
order: 99,
|
||||
})
|
||||
export class DemoDnsProvider extends AbstractDnsProvider<DemoRecord> {
|
||||
access!: DemoAccess;
|
||||
|
||||
async onInstance() {
|
||||
this.access = this.ctx.access as DemoAccess;
|
||||
// 也可以通过 ctx 成员变量传递 context
|
||||
this.logger.debug('access', this.access);
|
||||
// 初始化的操作
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 dns 解析记录,用于验证域名所有权
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions): Promise<any> {
|
||||
/**
|
||||
* options 参数说明
|
||||
* fullRecord: '_acme-challenge.example.com',
|
||||
* value: 一串 uuid
|
||||
* type: 'TXT',
|
||||
* domain: 'example.com'
|
||||
*/
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
|
||||
|
||||
try {
|
||||
// 调用创建 dns 解析记录的对应的云端接口,创建 txt 类型的 dns 解析记录
|
||||
// 请根据实际接口情况调用,例如:
|
||||
// const createDnsRecordUrl = "xxx"
|
||||
// const record = this.http.post(createDnsRecordUrl,{
|
||||
// // 授权参数
|
||||
// // 创建 dns 解析记录的参数
|
||||
// })
|
||||
// // 返回本次创建的 dns 解析记录,这个记录会在删除的时候用到
|
||||
// return record
|
||||
|
||||
// 模拟返回
|
||||
return { id: 'demo-record-id' };
|
||||
} catch (error) {
|
||||
this.logger.error('创建DNS记录失败:', error);
|
||||
throw new Error(`创建DNS记录失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 dns 解析记录,清理申请痕迹
|
||||
* @param options
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<DemoRecord>): Promise<void> {
|
||||
const { fullRecord, value, domain } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
this.logger.info('删除域名解析:', domain, fullRecord, value, record);
|
||||
|
||||
try {
|
||||
// 这里调用删除 txt dns 解析记录接口
|
||||
// 请根据实际接口情况调用,例如:
|
||||
// const deleteDnsRecordUrl = "xxx"
|
||||
// const res = this.http.delete(deleteDnsRecordUrl,{
|
||||
// // 授权参数
|
||||
// // 删除 dns 解析记录的参数
|
||||
// })
|
||||
|
||||
this.logger.info('删除域名解析成功:', fullRecord, value);
|
||||
} catch (error) {
|
||||
this.logger.error('删除DNS记录失败:', error);
|
||||
// 即使删除失败也不抛出异常,避免影响整个证书申请流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现获取域名列表
|
||||
*/
|
||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
try {
|
||||
const pager = new Pager(req);
|
||||
const res = await this.http.request({
|
||||
// 请求接口获取域名列表
|
||||
});
|
||||
const list =
|
||||
res.Domains?.map(item => ({
|
||||
id: item.Id,
|
||||
domain: item.DomainName,
|
||||
})) || [];
|
||||
|
||||
return {
|
||||
list,
|
||||
total: res.Total,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('获取域名列表失败:', error);
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化这个 provider,将其自动注册到系统中
|
||||
new DemoDnsProvider();
|
||||
```
|
||||
|
||||
### 示例 2: 阿里云 DNS Provider 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个阿里云 DNS Provider 插件,用于在 ACME 申请证书时添加和删除 TXT 解析记录。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions, PageSearch, PageRes, DomainRecord } from '@certd/plugin-cert';
|
||||
import { AliyunAccess } from './aliyun-access.js';
|
||||
import { Pager } from '@certd/pipeline';
|
||||
|
||||
type AliyunRecord = {
|
||||
RecordId: string;
|
||||
};
|
||||
|
||||
// 阿里云 DNS Provider 插件
|
||||
@IsDnsProvider({
|
||||
name: 'aliyun',
|
||||
title: '阿里云DNS',
|
||||
desc: '阿里云DNS提供商插件',
|
||||
icon: 'clarity:plugin-line',
|
||||
accessType: 'aliyun',
|
||||
order: 10,
|
||||
})
|
||||
export class AliyunDnsProvider extends AbstractDnsProvider<AliyunRecord> {
|
||||
access!: AliyunAccess;
|
||||
|
||||
async onInstance() {
|
||||
this.access = this.ctx.access as AliyunAccess;
|
||||
this.logger.debug('阿里云Access实例初始化成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 DNS 解析记录
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions): Promise<AliyunRecord> {
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('阿里云DNS: 添加解析记录', { fullRecord, value, type, domain });
|
||||
|
||||
try {
|
||||
// 提取主机记录
|
||||
const hostRecord = fullRecord.replace(`.${domain}`, '');
|
||||
|
||||
// 调用阿里云 API 创建解析记录
|
||||
const response = await this.access.doRequest({
|
||||
action: 'AddDomainRecord',
|
||||
data: {
|
||||
DomainName: domain,
|
||||
RR: hostRecord,
|
||||
Type: type,
|
||||
Value: value,
|
||||
TTL: 600, // 10分钟
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.info('阿里云DNS: 解析记录创建成功', { RecordId: response.RecordId });
|
||||
return { RecordId: response.RecordId };
|
||||
} catch (error) {
|
||||
this.logger.error('阿里云DNS: 创建解析记录失败', error);
|
||||
throw new Error(`阿里云DNS创建解析记录失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 DNS 解析记录
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<AliyunRecord>): Promise<void> {
|
||||
const { fullRecord, value, domain } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
this.logger.info('阿里云DNS: 删除解析记录', { fullRecord, value, domain, RecordId: record.RecordId });
|
||||
|
||||
try {
|
||||
// 调用阿里云 API 删除解析记录
|
||||
await this.access.doRequest({
|
||||
action: 'DeleteDomainRecord',
|
||||
data: {
|
||||
RecordId: record.RecordId,
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.info('阿里云DNS: 解析记录删除成功', { RecordId: record.RecordId });
|
||||
} catch (error) {
|
||||
this.logger.error('阿里云DNS: 删除解析记录失败', error);
|
||||
// 即使删除失败也不抛出异常,避免影响整个证书申请流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名列表
|
||||
*/
|
||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
try {
|
||||
const pager = new Pager(req);
|
||||
const response = await this.access.doRequest({
|
||||
action: 'DescribeDomains',
|
||||
data: {
|
||||
PageNumber: pager.page,
|
||||
PageSize: pager.pageSize,
|
||||
KeyWord: req.searchKey,
|
||||
},
|
||||
});
|
||||
|
||||
const list = response.Domains.Domain.map((domain: any) => ({
|
||||
id: domain.DomainId,
|
||||
domain: domain.DomainName,
|
||||
}));
|
||||
|
||||
return {
|
||||
list,
|
||||
total: response.TotalCount,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('阿里云DNS: 获取域名列表失败', error);
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
new AliyunDnsProvider();
|
||||
```
|
||||
|
||||
### 示例 3: 腾讯云 DNS Provider 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个腾讯云 DNS Provider 插件,用于在 ACME 申请证书时添加和删除 TXT 解析记录。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions, PageSearch, PageRes, DomainRecord } from '@certd/plugin-cert';
|
||||
import { TencentAccess } from './tencent-access.js';
|
||||
import { Pager } from '@certd/pipeline';
|
||||
|
||||
type TencentRecord = {
|
||||
RecordId: string;
|
||||
};
|
||||
|
||||
// 腾讯云 DNS Provider 插件
|
||||
@IsDnsProvider({
|
||||
name: 'tencent',
|
||||
title: '腾讯云DNS',
|
||||
desc: '腾讯云DNS提供商插件',
|
||||
icon: 'clarity:plugin-line',
|
||||
accessType: 'tencent',
|
||||
order: 20,
|
||||
})
|
||||
export class TencentDnsProvider extends AbstractDnsProvider<TencentRecord> {
|
||||
access!: TencentAccess;
|
||||
|
||||
async onInstance() {
|
||||
this.access = this.ctx.access as TencentAccess;
|
||||
this.logger.debug('腾讯云Access实例初始化成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 DNS 解析记录
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions): Promise<TencentRecord> {
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('腾讯云DNS: 添加解析记录', { fullRecord, value, type, domain });
|
||||
|
||||
try {
|
||||
// 提取主机记录
|
||||
const hostRecord = fullRecord.replace(`.${domain}`, '');
|
||||
|
||||
// 调用腾讯云 API 创建解析记录
|
||||
const response = await this.access.doRequest({
|
||||
action: 'CreateRecord',
|
||||
data: {
|
||||
Domain: domain,
|
||||
SubDomain: hostRecord,
|
||||
RecordType: type,
|
||||
RecordValue: value,
|
||||
TTL: 600, // 10分钟
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.info('腾讯云DNS: 解析记录创建成功', { RecordId: response.RecordId });
|
||||
return { RecordId: response.RecordId };
|
||||
} catch (error) {
|
||||
this.logger.error('腾讯云DNS: 创建解析记录失败', error);
|
||||
throw new Error(`腾讯云DNS创建解析记录失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 DNS 解析记录
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<TencentRecord>): Promise<void> {
|
||||
const { fullRecord, value, domain } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
this.logger.info('腾讯云DNS: 删除解析记录', { fullRecord, value, domain, RecordId: record.RecordId });
|
||||
|
||||
try {
|
||||
// 调用腾讯云 API 删除解析记录
|
||||
await this.access.doRequest({
|
||||
action: 'DeleteRecord',
|
||||
data: {
|
||||
RecordId: record.RecordId,
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.info('腾讯云DNS: 解析记录删除成功', { RecordId: record.RecordId });
|
||||
} catch (error) {
|
||||
this.logger.error('腾讯云DNS: 删除解析记录失败', error);
|
||||
// 即使删除失败也不抛出异常,避免影响整个证书申请流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名列表
|
||||
*/
|
||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
try {
|
||||
const pager = new Pager(req);
|
||||
const response = await this.access.doRequest({
|
||||
action: 'DescribeDomains',
|
||||
data: {
|
||||
Offset: (pager.page - 1) * pager.pageSize,
|
||||
Limit: pager.pageSize,
|
||||
Keyword: req.searchKey,
|
||||
},
|
||||
});
|
||||
|
||||
const list = response.Domains.map((domain: any) => ({
|
||||
id: domain.DomainId,
|
||||
domain: domain.DomainName,
|
||||
}));
|
||||
|
||||
return {
|
||||
list,
|
||||
total: response.TotalCount,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('腾讯云DNS: 获取域名列表失败', error);
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
new TencentDnsProvider();
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
2. **accessType**:必须指定对应的云平台的 access 类型名称。
|
||||
3. **记录结构**:定义适合对应云平台的记录数据结构,至少包含 id 字段用于删除记录。
|
||||
4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`,参数文本化,不要传对象,否则会输出`[object Object]}`。
|
||||
5. **错误处理**:API 调用失败时应抛出明确的错误信息。
|
||||
6. **旧版数据兼容**: 新增插件参数,必须要考虑旧版数据兼容,比如新增一个deployType参数,有两种值:`default`和`custom`,需要在使用时判空,走旧版逻辑。
|
||||
@@ -0,0 +1 @@
|
||||
我需要开发一个 DNS Provider 插件,用于在 ACME 申请证书时添加 TXT 解析记录。请指导我如何实现。
|
||||
@@ -0,0 +1,121 @@
|
||||
# DNS Provider 插件开发指南
|
||||
|
||||
## 开发步骤
|
||||
|
||||
### 1. 导入必要的依赖
|
||||
|
||||
```typescript
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { DemoAccess } from './access.js';
|
||||
import { isDev } from '../../utils/env.js';
|
||||
```
|
||||
|
||||
### 2. 定义记录数据结构
|
||||
|
||||
```typescript
|
||||
type DemoRecord = {
|
||||
// 这里定义 Record 记录的数据结构,跟对应云平台接口返回值一样即可,一般是拿到 id 就行,用于删除 txt 解析记录,清理申请痕迹
|
||||
// id:string
|
||||
};
|
||||
```
|
||||
|
||||
### 3. 使用 @IsDnsProvider 注解注册插件
|
||||
|
||||
```typescript
|
||||
// 这里通过 IsDnsProvider 注册一个 dnsProvider
|
||||
@IsDnsProvider({
|
||||
name: 'demo', // 插件唯一标识
|
||||
title: 'Dns提供商Demo', // 插件标题
|
||||
desc: 'dns provider示例', // 插件描述
|
||||
icon: 'clarity:plugin-line', // 插件图标
|
||||
// 这里是对应的云平台的 access 类型名称
|
||||
accessType: 'demo',
|
||||
order: 99, // 排序
|
||||
})
|
||||
export class DemoDnsProvider extends AbstractDnsProvider<DemoRecord> {
|
||||
access!: DemoAccess;
|
||||
|
||||
async onInstance() {
|
||||
this.access = this.ctx.access as DemoAccess;
|
||||
// 也可以通过 ctx 成员变量传递 context
|
||||
this.logger.debug('access', this.access);
|
||||
// 初始化的操作
|
||||
// ...
|
||||
}
|
||||
|
||||
// 插件实现...
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 实现 createRecord 方法
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 创建 dns 解析记录,用于验证域名所有权
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions): Promise<any> {
|
||||
/**
|
||||
* options 参数说明
|
||||
* fullRecord: '_acme-challenge.example.com',
|
||||
* value: 一串 uuid
|
||||
* type: 'TXT',
|
||||
* domain: 'example.com'
|
||||
*/
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
|
||||
|
||||
// 调用创建 dns 解析记录的对应的云端接口,创建 txt 类型的 dns 解析记录
|
||||
// 请根据实际接口情况调用,例如:
|
||||
// const createDnsRecordUrl = "xxx"
|
||||
// const record = this.http.post(createDnsRecordUrl,{
|
||||
// // 授权参数
|
||||
// // 创建 dns 解析记录的参数
|
||||
// })
|
||||
// // 返回本次创建的 dns 解析记录,这个记录会在删除的时候用到
|
||||
// return record
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 实现 removeRecord 方法
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 删除 dns 解析记录,清理申请痕迹
|
||||
* @param options
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<DemoRecord>): Promise<void> {
|
||||
const { fullRecord, value, domain } = options.recordReq;
|
||||
const record = options.recordRes;
|
||||
this.logger.info('删除域名解析:', domain, fullRecord, value, record);
|
||||
// 这里调用删除 txt dns 解析记录接口
|
||||
// 请根据实际接口情况调用,例如:
|
||||
|
||||
// const deleteDnsRecordUrl = "xxx"
|
||||
// const res = this.http.delete(deleteDnsRecordUrl,{
|
||||
// // 授权参数
|
||||
// // 删除 dns 解析记录的参数
|
||||
// })
|
||||
|
||||
|
||||
this.logger.info('删除域名解析成功:', fullRecord, value);
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 实例化插件
|
||||
|
||||
```typescript
|
||||
// 实例化这个 provider,将其自动注册到系统中
|
||||
if (isDev()) {
|
||||
// 你的实现 要去掉这个 if,不然生产环境将不会显示
|
||||
new DemoDnsProvider();
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
2. **accessType**:必须指定对应的云平台的 access 类型名称。
|
||||
3. **记录结构**:定义适合对应云平台的记录数据结构,至少包含 id 字段用于删除记录。
|
||||
4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。
|
||||
5. **错误处理**:API 调用失败时应抛出明确的错误信息。
|
||||
6. **实例化**:生产环境中应移除 `if (isDev())` 条件,确保插件在生产环境中也能被注册。
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
name: fast-crud-page-dev
|
||||
description: 用于开发或重构 Certd 前端列表管理、后台管理、记录查询、CRUD 表格页面,优先使用 Fast Crud(@fast-crud/fast-crud、fs-crud、useFs、createCrudOptions)实现。当用户要求列表页、管理页、审核页、记录页或表格 CRUD 页面时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Fast Crud 页面开发技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 前端列表管理页面开发专家,熟悉 Vue 3、Ant Design Vue、Fast Crud 和本仓库现有页面拆分方式。你的目标是让管理页面保持统一的表格、搜索、分页、操作列和弹窗体验。
|
||||
|
||||
## 核心规则
|
||||
|
||||
- 列表管理、后台管理、记录查询、审核记录、CRUD 表格类页面,默认优先使用 Fast Crud 实现。
|
||||
- 只有轻量只读展示、强交互自定义界面、复杂可视化或已有页面模式明确不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明原因。
|
||||
- 设置表单、概览卡片、向导流程等非列表主体可以保留自定义 Vue;如果同一功能同时包含设置和列表,优先拆成独立页面,或把设置放入对话框。
|
||||
|
||||
## 推荐文件拆分
|
||||
|
||||
- `api.ts`:封装接口请求,保持页面和 CRUD 配置里不直接散落 URL。
|
||||
- `crud.tsx` / `crud-*.tsx`:导出 `createCrudOptions`,集中定义请求映射、搜索项、列、表单、操作列、工具栏和字典。
|
||||
- `index.vue`:承载 `fs-page`、`fs-crud`、页面头部、弹窗和生命周期,使用 `useFs({ createCrudOptions, context })` 创建绑定。
|
||||
|
||||
## 实现流程
|
||||
|
||||
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
|
||||
2. 在 `index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` 或 `onActivated` 时调用 `crudExpose.doRefresh()`;两个生命周期同时存在时只保留一个刷新入口,避免首次进入页面请求两次。
|
||||
3. 在 `crud.tsx` 中配置 `request.pageRequest`、`columns`、`search`、`form`、`rowHandle`、`actionbar`、`toolbar` 等,接口分页参数和返回值按现有页面适配。
|
||||
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons` 或 `actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
|
||||
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
|
||||
6. 表格查询条件使用 Fast Crud 的 `search` 配置;新增/编辑表单使用 Fast Crud 的 `form` 配置,复杂设置项可以用 Ant Design Vue 对话框承载。
|
||||
7. 删除、审核通过、拒绝等危险操作必须保留确认弹窗和错误提示,成功后刷新当前 CRUD 列表。
|
||||
8. 对话框里只做纯确认时可以使用 `Modal.confirm`;只要需要字段输入、表单校验或提交字段,统一使用 `useFormDialog` / `openFormDialog`,不要在 `Modal.confirm` 的 `content` 里手写输入框。
|
||||
|
||||
## crud 配置
|
||||
|
||||
const crudOptions ={
|
||||
id: string, //表格唯一标识,同一个页面的多个表格的列设置和字段设置会根据id进行区分保存
|
||||
request:{}, //http请求
|
||||
columns:{ //字段配置
|
||||
key:{ //字段key
|
||||
column:{}, //对应table-column配置
|
||||
form:{}, //表单中该字段的公共配置,viewForm、addForm、editForm、search会集成此配置,支持对应ui的form-item配置
|
||||
viewForm:{}, //查看表单中该字段的配置,支持对应ui的form-item配置
|
||||
addForm:{}, // 添加表单中该字段的配置,支持对应ui的form-item配置
|
||||
editForm:{}, //编辑表单中该字段的配置,支持对应ui的form-item配置
|
||||
search:{} //对应查询表单的form-item配置
|
||||
}
|
||||
},
|
||||
search:{ //查询框配置 ,对应fs-search组件
|
||||
options:{} //查询表单配置 ,对应el-from, a-form配置
|
||||
},
|
||||
actionbar:{}, //动作条,添加按钮,对应fs-actionbar组件
|
||||
toolbar:{}, //工具条 ,对应fs-toolbar组件
|
||||
table:{ //表格配置,对应fs-table
|
||||
// 对应 el-table / a-table的配置
|
||||
slots:{} // 对应el-table ,a-table的插槽
|
||||
},
|
||||
data:{}, //列表数据,无需配置,自动从pageRequest中获取
|
||||
// 如果你要手动改变表格数据,可以通过crudBinding.value.data直接赋值修改表格数据
|
||||
rowHandle:{}, //操作列配置,对应fs-row-handle
|
||||
form:{ //表单的公共配置,对应el-form,a-form配置
|
||||
wrapper:{} //表单外部容器(对话框)的配置,对应el-dialog,el-drawer,a-model,a-drawer的配置
|
||||
},
|
||||
viewForm:{}, //查看表单的独立配置
|
||||
editForm:{}, //编辑表单的独立配置
|
||||
addForm:{}, //添加表单的独立配置
|
||||
pagination:{}, //分页配置 ,对应el-pagination / a-pagination
|
||||
container:{}, //容器配置 ,对应fs-container
|
||||
}
|
||||
|
||||
## 布局高度
|
||||
|
||||
- Fast Crud 表格依赖外部容器高度计算。虽然表格本身有默认约 200px 高度,但页面内嵌 `fs-crud` 时,为了获得稳定可用的列表区域,必须让外层容器提供明确高度或剩余高度。
|
||||
- 独立列表页通常可直接让 `fs-page` / 页面内容区撑满;如果表格嵌在 tabs、详情页、上下分区或弹窗里,要从页面根容器到 `fs-crud` 建立完整的 flex 高度链路:父容器 `display: flex; flex-direction: column; min-height: 0`,中间内容区和 tab pane 使用 `flex: 1; min-height: 0`,`fs-crud` 本身也使用 `flex: 1; min-height: 0`。
|
||||
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
|
||||
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
|
||||
|
||||
## 列表导出
|
||||
|
||||
- 列表需要导出时,优先使用 Fast Crud 工具栏导出能力,不要另写一套导出按钮或后端接口,除非数据必须跨权限、跨分页或异步生成文件。
|
||||
- 导出当前搜索条件下的数据时,在 `toolbar.export` 中设置 `dataFrom: "search"`,并显式打开导出按钮。
|
||||
- 导出列必须输出 Excel 可读的纯文本或数字;不要直接导出对象、数组、VNode、进度条组件、开关组件、时间戳毫秒值等。
|
||||
- 有隐藏但业务上需要导出的字段时,把字段定义为普通列并设置 `column.show: false`,再在 `columnFilter` 中对该字段返回 `true`。例如证书域名这类只用于导出的辅助列。
|
||||
- 嵌套字段可以使用 `lastVars.certDomains` 这类 key;导出格式化时用安全取值函数读取嵌套值。
|
||||
- `dataFormatter` 中统一格式化特殊字段:时间字段转 `YYYY-MM-DD HH:mm:ss`,日期类有效期转业务文案或 `YYYY-MM-DD`,枚举/开关转字典 label,数组转逗号分隔字符串,对象转明确的业务摘要。
|
||||
|
||||
```typescript
|
||||
import { ColumnProps, DataFormatterContext } from "@fast-crud/fast-crud";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
function getRecordValue(row: any, key: string) {
|
||||
return key.split(".").reduce((target, item) => target?.[item], row);
|
||||
}
|
||||
|
||||
function formatListValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(",");
|
||||
}
|
||||
return value ?? "";
|
||||
}
|
||||
|
||||
function exportColumnFilter(col: ColumnProps) {
|
||||
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
|
||||
return false;
|
||||
}
|
||||
if (col.key === "lastVars.certDomains") {
|
||||
return true;
|
||||
}
|
||||
return col.show !== false;
|
||||
}
|
||||
|
||||
function exportDataFormatter(opts: DataFormatterContext) {
|
||||
const { row, originalRow, col, exportCol } = opts;
|
||||
const key = col.key;
|
||||
const value = getRecordValue(originalRow, key);
|
||||
|
||||
if (key === "lastVars.certDomains") {
|
||||
row[key] = formatListValue(value);
|
||||
} else if (key.includes("Time") && value) {
|
||||
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
if (col.width) {
|
||||
exportCol.width = col.width / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
toolbar: {
|
||||
buttons: {
|
||||
export: { show: true },
|
||||
},
|
||||
export: {
|
||||
dataFrom: "search",
|
||||
columnFilter: exportColumnFilter,
|
||||
dataFormatter: exportDataFormatter,
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
"lastVars.certDomains": {
|
||||
title: "证书域名",
|
||||
type: "text",
|
||||
column: {
|
||||
show: false,
|
||||
width: 260,
|
||||
ellipsis: true,
|
||||
},
|
||||
form: { show: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 内置 CRUD 按钮
|
||||
|
||||
只要在 `request` 中配置了 `addRequest`、`editRequest`、`delRequest`,Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm`、`openEditDialog` 等方法**。
|
||||
|
||||
```typescript
|
||||
// crud.tsx
|
||||
const addRequest = async ({ form }: AddReq) => await api.AddObj(form);
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => await api.DelObj(row.id);
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: { pageRequest, addRequest, editRequest, delRequest },
|
||||
rowHandle: {
|
||||
buttons: {
|
||||
view: { show: false }, // 不需要查看就隐藏
|
||||
edit: {}, // 自动调用 editRequest
|
||||
remove: {}, // 自动调用 delRequest,自带确认弹窗和错误提示
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
- 删除按钮自带确认弹窗,不需要额外包装 `Modal.confirm`。
|
||||
- 只有**自定义操作**(如禁用、审核、生成激活码)才需要在 `rowHandle.buttons` 中手写 `click` 处理方法。
|
||||
- 如果不需要某列操作,直接把对应 key 去掉或设 `show: false`。
|
||||
|
||||
## compute 动态计算
|
||||
|
||||
当 `rowHandle.buttons` 的 `show`、`disabled` 等属性需要根据行数据动态决定时,**必须使用 `compute` 包裹**,不能直接传函数。
|
||||
|
||||
```typescript
|
||||
import { compute } from "@fast-crud/fast-crud";
|
||||
|
||||
// WRONG: 直接传函数
|
||||
show: ({ row }) => row.status === "unused"
|
||||
|
||||
// CORRECT: 用 compute 包裹
|
||||
show: compute(({ row }) => row.status === "unused")
|
||||
```
|
||||
|
||||
`compute` 基于 Vue 的 `computed`,但额外支持上下文参数。适用位置:
|
||||
- `rowHandle.buttons` 的 `show`、`disabled` 等属性
|
||||
- `columns.key.column` 的 `show`、`cellRender` 等
|
||||
- `columns.key.form` / `search` 的表单字段属性
|
||||
|
||||
参考文档:http://fast-crud.docmirror.cn/guide/advance/compute.html
|
||||
|
||||
## 代码习惯
|
||||
|
||||
- 页面命名、API 命名、权限标识和路由结构要贴近同目录已有页面。
|
||||
- CRUD 配置中不要写大段业务流程;复杂逻辑放回 `index.vue` 方法或 `api.ts`。
|
||||
- 能用 `dict`、`compute`、`valueBuilder`、`valueResolve`、`component` 配置表达的表格/表单行为,不要改成手写模板。
|
||||
- 保持列表页密度和操作入口克制,不要做营销式布局、嵌套卡片或大块说明文字。
|
||||
- 如果页面有“设置 + 列表”,管理端优先拆成两个路由页面;用户端提现设置这类低频配置优先使用对话框保存。
|
||||
|
||||
## 验证方式
|
||||
|
||||
- 前端改动后,只对本次改动的 Vue / TS / TSX / locale 文件运行项目现有 Prettier / ESLint。
|
||||
- 不运行 `vue-tsc` / `pnpm tsc`,因为当前依赖组合下 `vue-tsc` 已知会抛内部错误。
|
||||
- 若只是新增或修改本 skill / 文档,不需要运行前端格式化和测试。
|
||||
@@ -0,0 +1,294 @@
|
||||
---
|
||||
name: plugin-converter
|
||||
description: 用于将 Certd 插件转换为 YAML 配置文件的命令行工具,支持分析单个插件文件、识别插件类型并生成对应的 YAML 配置。当用户需要生成插件配置、转换插件格式、批量处理插件或修改现有插件配置时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# 插件转换工具技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 插件开发专家,擅长使用插件转换工具将 Certd 插件转换为 YAML 配置文件,熟悉命令行工具的使用和 Certd 插件开发规范。
|
||||
|
||||
## 核心指令
|
||||
|
||||
请严格按照以下步骤执行任务:
|
||||
|
||||
1. **定位工具位置**
|
||||
|
||||
- 工具位于 `trae/skills/convert-plugin-to-yaml.js`
|
||||
|
||||
2. **了解功能特性**
|
||||
|
||||
- 单个插件转换:支持指定单个插件文件进行转换
|
||||
- 批量插件转换:支持指定目录批量转换多个插件
|
||||
- 自动类型识别:自动识别插件类型(Access、Task、DNS Provider、Notification、Addon)
|
||||
- 详细日志输出:提供详细的转换过程日志
|
||||
- YAML 配置生成:生成标准的 YAML 配置文件
|
||||
- 配置文件保存:自动将生成的配置保存到 `./metadata` 目录
|
||||
- 自定义输出目录:支持指定自定义输出目录
|
||||
- 格式化输出:支持格式化 YAML 输出
|
||||
- 可复用函数:导出了可复用的函数,便于其他模块调用
|
||||
|
||||
3. **使用工具**
|
||||
|
||||
- 基本用法:`node trae/skills/convert-plugin-to-yaml.js <插件文件路径>`
|
||||
- 批量转换:`node trae/skills/convert-plugin-to-yaml.js <目录路径>`
|
||||
- 自定义输出目录:`node trae/skills/convert-plugin-to-yaml.js <插件文件路径> --output <输出目录>`
|
||||
- 格式化输出:`node trae/skills/convert-plugin-to-yaml.js <插件文件路径> --format`
|
||||
- 示例:
|
||||
- 转换 Access 插件:`node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js`
|
||||
- 转换 Task 插件:`node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js`
|
||||
- 转换 DNS Provider 插件:`node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js`
|
||||
- 批量转换插件:`node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/`
|
||||
- 自定义输出目录:`node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js --output ./configs`
|
||||
|
||||
4. **理解转换过程**
|
||||
|
||||
- 加载插件模块:使用 `import()` 动态加载指定的插件文件
|
||||
- 分析插件定义:检查模块导出的对象,寻找带有 `define` 属性的插件
|
||||
- 识别插件类型:根据插件的继承关系或属性识别插件类型
|
||||
- 生成 YAML 配置:基于插件定义生成标准的 YAML 配置
|
||||
- 保存配置文件:将生成的配置保存到 `./metadata` 目录
|
||||
|
||||
5. **了解输出说明**
|
||||
|
||||
- 命令行输出:插件加载状态、插件导出的对象列表、插件类型识别结果、生成的 YAML 配置内容、配置文件保存路径
|
||||
- 配置文件命名规则:`<插件类型>[_<子类型>]_<插件名称>.yaml`
|
||||
|
||||
6. **理解插件类型识别逻辑**
|
||||
|
||||
- DNS Provider:如果插件定义中包含 `accessType` 属性
|
||||
- Task:如果插件继承自 `AbstractTaskPlugin`
|
||||
- Notification:如果插件继承自 `BaseNotification`
|
||||
- Access:如果插件继承自 `BaseAccess`
|
||||
- Addon:如果插件继承自 `BaseAddon`
|
||||
|
||||
7. **遵循注意事项**
|
||||
- 文件路径:插件文件路径可以是相对路径或绝对路径
|
||||
- 文件格式:仅支持 `.js` 文件,不支持 `.ts` 文件(需要先编译)
|
||||
- 依赖安装:执行前确保已安装所有必要的依赖
|
||||
- 配置目录:如果 `./metadata` 目录不存在,工具会自动创建
|
||||
- 错误处理:如果插件加载失败或识别失败,工具会输出错误信息但不会终止执行
|
||||
|
||||
## 输出规范
|
||||
|
||||
- 必须包含工具的使用方法和示例
|
||||
- 必须包含转换过程的详细说明
|
||||
- 必须包含输出说明和配置文件命名规则
|
||||
- 必须包含插件类型识别逻辑
|
||||
- 必须包含注意事项和故障排除建议
|
||||
|
||||
## 示例
|
||||
|
||||
### 示例 1: 转换单个 Access 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
将 Access 插件转换为 YAML 配置文件。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```bash
|
||||
# 转换 Access 插件
|
||||
node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
```
|
||||
|
||||
#### 输出
|
||||
|
||||
```bash
|
||||
$ node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
插件模块导出了 1 个对象: DemoAccess
|
||||
处理插件: DemoAccess
|
||||
插件类型: access
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: demo
|
||||
title: 授权插件示例
|
||||
desc: 这是一个示例授权插件,用于演示如何实现一个授权插件
|
||||
icon: clarity:plugin-line
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
YAML 配置已保存到: ./metadata/access_demo.yaml
|
||||
插件转换完成!
|
||||
```
|
||||
|
||||
### 示例 2: 批量转换插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
批量转换目录中的所有插件为 YAML 配置文件。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```bash
|
||||
# 批量转换插件
|
||||
node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/
|
||||
```
|
||||
|
||||
#### 输出
|
||||
|
||||
```bash
|
||||
$ node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/
|
||||
开始转换目录: packages/ui/certd-server/src/plugins/
|
||||
|
||||
正在处理文件: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
插件模块导出了 1 个对象: DemoAccess
|
||||
处理插件: DemoAccess
|
||||
插件类型: access
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: demo
|
||||
title: 授权插件示例
|
||||
desc: 这是一个示例授权插件,用于演示如何实现一个授权插件
|
||||
icon: clarity:plugin-line
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
YAML 配置已保存到: ./metadata/access_demo.yaml
|
||||
插件转换完成!
|
||||
|
||||
正在处理文件: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js
|
||||
插件模块导出了 1 个对象: DemoTest
|
||||
处理插件: DemoTest
|
||||
插件类型: task
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: DemoTest
|
||||
title: Demo-测试插件
|
||||
desc: 这是一个示例任务插件,用于演示如何实现一个任务插件
|
||||
icon: clarity:plugin-line
|
||||
pluginType: task
|
||||
group: other
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js
|
||||
|
||||
YAML 配置已保存到: ./metadata/task_DemoTest.yaml
|
||||
插件转换完成!
|
||||
|
||||
正在处理文件: packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js
|
||||
插件模块导出了 1 个对象: DemoDnsProvider
|
||||
处理插件: DemoDnsProvider
|
||||
插件类型: dns-provider
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: demo
|
||||
title: Dns提供商Demo
|
||||
desc: dns provider示例
|
||||
icon: clarity:plugin-line
|
||||
pluginType: dns-provider
|
||||
accessType: demo
|
||||
order: 99
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js
|
||||
|
||||
YAML 配置已保存到: ./metadata/dns-provider_demo.yaml
|
||||
插件转换完成!
|
||||
|
||||
批量转换完成,共处理 3 个插件文件
|
||||
```
|
||||
|
||||
### 示例 3: 自定义输出目录
|
||||
|
||||
#### 用户输入
|
||||
|
||||
将插件转换为 YAML 配置文件,并保存到自定义目录。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```bash
|
||||
# 自定义输出目录
|
||||
node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js --output ./configs
|
||||
```
|
||||
|
||||
#### 输出
|
||||
|
||||
```bash
|
||||
$ node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js --output ./configs
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
插件模块导出了 1 个对象: DemoAccess
|
||||
处理插件: DemoAccess
|
||||
插件类型: access
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: demo
|
||||
title: 授权插件示例
|
||||
desc: 这是一个示例授权插件,用于演示如何实现一个授权插件
|
||||
icon: clarity:plugin-line
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
YAML 配置已保存到: ./configs/access_demo.yaml
|
||||
插件转换完成!
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **模块加载失败**
|
||||
|
||||
- 原因:插件文件依赖未安装或路径错误
|
||||
- 解决:确保已安装所有依赖,检查文件路径是否正确
|
||||
|
||||
2. **插件类型识别失败**
|
||||
|
||||
- 原因:插件未正确继承基类或缺少必要的属性
|
||||
- 解决:检查插件代码,确保正确继承对应的基类
|
||||
|
||||
3. **YAML 配置生成失败**
|
||||
|
||||
- 原因:插件定义格式不正确
|
||||
- 解决:检查插件的 `define` 属性格式是否正确
|
||||
|
||||
4. **配置文件保存失败**
|
||||
- 原因:权限不足或磁盘空间不足
|
||||
- 解决:确保有足够的权限和磁盘空间
|
||||
|
||||
### 调试建议
|
||||
|
||||
- **查看详细日志**:工具会输出详细的转换过程日志,仔细查看日志信息
|
||||
- **检查插件代码**:确保插件代码符合 Certd 插件开发规范
|
||||
- **尝试简化插件**:如果转换失败,尝试创建一个最小化的插件示例进行测试
|
||||
- **检查依赖版本**:确保使用的依赖版本与 Certd 兼容
|
||||
|
||||
## 代码结构
|
||||
|
||||
### 主要函数
|
||||
|
||||
1. **isPrototypeOf(value, cls)**:检查对象是否是指定类的原型
|
||||
2. **loadSingleModule(filePath)**:加载单个插件模块
|
||||
3. **convertSinglePlugin(pluginPath)**:分析单个插件并生成 YAML 配置
|
||||
4. **main()**:主函数,处理命令行参数并执行转换
|
||||
|
||||
### 导出函数
|
||||
|
||||
工具导出了以下函数,便于其他模块调用:
|
||||
|
||||
```javascript
|
||||
export {
|
||||
convertSinglePlugin, // 转换单个插件
|
||||
loadSingleModule, // 加载单个模块
|
||||
isPrototypeOf, // 检查原型关系
|
||||
};
|
||||
```
|
||||
|
||||
## 应用场景
|
||||
|
||||
1. **插件开发**:在开发新插件时,快速生成配置文件
|
||||
2. **插件调试**:查看插件的内部定义和配置
|
||||
3. **插件管理**:批量转换现有插件为标准配置格式
|
||||
4. **自动化构建**:集成到构建流程中,自动生成插件配置
|
||||
@@ -0,0 +1 @@
|
||||
我需要将一个插件转换为 YAML 配置文件。请指导我如何使用插件转换工具。
|
||||
@@ -0,0 +1,96 @@
|
||||
# 插件转换工具使用指南
|
||||
|
||||
## 工具说明
|
||||
|
||||
插件转换工具用于将单个 Certd 插件转换为 YAML 配置文件,方便插件的注册和管理。
|
||||
|
||||
## 工具位置
|
||||
|
||||
`.trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js`
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js <插件文件路径>
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
#### 转换 Access 插件
|
||||
|
||||
```bash
|
||||
node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
```
|
||||
|
||||
#### 转换 Task 插件
|
||||
|
||||
```bash
|
||||
node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js
|
||||
```
|
||||
|
||||
#### 转换 DNS Provider 插件
|
||||
|
||||
```bash
|
||||
node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js
|
||||
```
|
||||
|
||||
## 转换过程
|
||||
|
||||
1. **加载插件模块**:使用 `import()` 动态加载指定的插件文件
|
||||
2. **分析插件定义**:检查模块导出的对象,寻找带有 `define` 属性的插件
|
||||
3. **识别插件类型**:根据插件的继承关系或属性识别插件类型
|
||||
4. **生成 YAML 配置**:基于插件定义生成标准的 YAML 配置
|
||||
5. **保存配置文件**:将生成的配置保存到 `./metadata` 目录
|
||||
|
||||
## 输出说明
|
||||
|
||||
### 命令行输出
|
||||
|
||||
执行转换命令后,工具会输出以下信息:
|
||||
|
||||
- 插件加载状态
|
||||
- 插件导出的对象列表
|
||||
- 插件类型识别结果
|
||||
- 生成的 YAML 配置内容
|
||||
- 配置文件保存路径
|
||||
|
||||
### 配置文件命名规则
|
||||
|
||||
生成的配置文件命名规则为:
|
||||
|
||||
```
|
||||
<插件类型>[_<子类型>]_<插件名称>.yaml
|
||||
```
|
||||
|
||||
例如:
|
||||
|
||||
- `access_demo.yaml`(Access 插件)
|
||||
- `deploy_DemoTest.yaml`(Task 插件)
|
||||
- `dnsProvider_demo.yaml`(DNS Provider 插件)
|
||||
|
||||
## 示例输出
|
||||
|
||||
### 转换 Access 插件示例
|
||||
|
||||
```bash
|
||||
$ node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
插件模块导出了 1 个对象: DemoAccess
|
||||
处理插件: DemoAccess
|
||||
插件类型: access
|
||||
脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
生成的 YAML 配置:
|
||||
name: demo
|
||||
title: 授权插件示例
|
||||
desc: 这是一个示例授权插件,用于演示如何实现一个授权插件
|
||||
icon: clarity:plugin-line
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js
|
||||
|
||||
YAML 配置已保存到: ./metadata/access_demo.yaml
|
||||
插件转换完成!
|
||||
```
|
||||
@@ -0,0 +1,155 @@
|
||||
// 转换单个插件为 YAML 配置的技能脚本
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { AbstractTaskPlugin, BaseAccess, BaseNotification } from '@certd/pipeline';
|
||||
import { BaseAddon } from '@certd/lib-server';
|
||||
|
||||
/**
|
||||
* 检查对象是否是指定类的原型
|
||||
*/
|
||||
function isPrototypeOf(value, cls) {
|
||||
return cls.prototype.isPrototypeOf(value.prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载单个插件模块
|
||||
*/
|
||||
async function loadSingleModule(filePath) {
|
||||
try {
|
||||
// 转换为 file:// URL(Windows 必需)
|
||||
const moduleUrl = pathToFileURL(filePath).href;
|
||||
const module = await import(moduleUrl);
|
||||
return module.default || module;
|
||||
} catch (err) {
|
||||
console.error(`加载模块 ${filePath} 失败:`, err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析单个插件并生成 YAML 配置
|
||||
*/
|
||||
async function convertSinglePlugin(pluginPath) {
|
||||
console.log(`开始转换插件: ${pluginPath}`);
|
||||
|
||||
// 加载插件模块
|
||||
const module = await loadSingleModule(pluginPath);
|
||||
if (!module) {
|
||||
console.error('加载插件失败,退出');
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模块中的所有导出
|
||||
const entry = Object.entries(module);
|
||||
if (entry.length === 0) {
|
||||
console.error('插件模块没有导出任何内容');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`插件模块导出了 ${entry.length} 个对象: ${entry.map(([name]) => name).join(', ')}`);
|
||||
|
||||
// 处理每个导出的对象
|
||||
for (const [name, value] of entry) {
|
||||
// 检查是否是插件(有 define 属性)
|
||||
if (!value.define) {
|
||||
console.log(`跳过非插件对象: ${name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`处理插件: ${name}`);
|
||||
|
||||
// 构建插件定义
|
||||
const pluginDefine = {
|
||||
...value.define,
|
||||
};
|
||||
|
||||
let subType = '';
|
||||
|
||||
// 确定插件类型
|
||||
if (pluginDefine.accessType) {
|
||||
pluginDefine.pluginType = 'dnsProvider';
|
||||
} else if (isPrototypeOf(value, AbstractTaskPlugin)) {
|
||||
pluginDefine.pluginType = 'deploy';
|
||||
} else if (isPrototypeOf(value, BaseNotification)) {
|
||||
pluginDefine.pluginType = 'notification';
|
||||
} else if (isPrototypeOf(value, BaseAccess)) {
|
||||
pluginDefine.pluginType = 'access';
|
||||
} else if (isPrototypeOf(value, BaseAddon)) {
|
||||
pluginDefine.pluginType = 'addon';
|
||||
subType = '_' + (pluginDefine.addonType || '');
|
||||
} else {
|
||||
console.log(`[warning] 未知的插件类型:${pluginDefine.name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginDefine.type = 'builtIn';
|
||||
|
||||
// 计算脚本文件路径
|
||||
const relativePath = path.relative(process.cwd(), pluginPath);
|
||||
const scriptFilePath = relativePath.replace(/\\/g, '/').replace(/\.js$/, '.js');
|
||||
pluginDefine.scriptFilePath = scriptFilePath;
|
||||
|
||||
console.log(`插件类型: ${pluginDefine.pluginType}`);
|
||||
console.log(`脚本路径: ${scriptFilePath}`);
|
||||
|
||||
// 生成 YAML 配置
|
||||
const yamlContent = yaml.dump(pluginDefine);
|
||||
console.log('\n生成的 YAML 配置:');
|
||||
console.log(yamlContent);
|
||||
|
||||
// 可选:保存到文件
|
||||
const outputDir = './metadata';
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
const outputFileName = `${pluginDefine.pluginType}${subType}_${pluginDefine.name}.yaml`;
|
||||
const outputPath = path.join(outputDir, outputFileName);
|
||||
|
||||
fs.writeFileSync(outputPath, yamlContent, 'utf8');
|
||||
console.log(`\nYAML 配置已保存到: ${outputPath}`);
|
||||
|
||||
return pluginDefine;
|
||||
}
|
||||
|
||||
console.error('未找到有效的插件定义');
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error('请指定插件文件路径');
|
||||
console.log('用法: node convert-plugin-to-yaml.js <插件文件路径>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pluginPath = args[0];
|
||||
|
||||
if (!fs.existsSync(pluginPath)) {
|
||||
console.error(`插件文件不存在: ${pluginPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await convertSinglePlugin(pluginPath);
|
||||
console.log('\n插件转换完成!');
|
||||
} catch (error) {
|
||||
console.error('转换过程中出错:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
main();
|
||||
}
|
||||
|
||||
// 导出函数,以便其他模块使用
|
||||
export { convertSinglePlugin, loadSingleModule, isPrototypeOf };
|
||||
@@ -0,0 +1,669 @@
|
||||
---
|
||||
name: task-plugin-dev
|
||||
description: 用于开发 Certd 系统中的 Task 插件,继承自 AbstractTaskPlugin 类,被流水线调用 execute 方法将证书部署到对应的应用上。当用户需要创建任务插件、部署证书、自动化任务或修改现有 Task 插件时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Task 插件开发技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 插件开发专家,擅长创建和实现 Task 类型的插件,熟悉 TypeScript 编程和 Certd 插件开发规范。
|
||||
|
||||
## 核心指令
|
||||
|
||||
请严格按照以下步骤执行任务:
|
||||
|
||||
1. **导入必要的依赖**
|
||||
|
||||
- 导入 `AbstractTaskPlugin`, `IsTaskPlugin`, `PageSearch`, `pluginGroups`, `RunStrategy`, `TaskInput` 等必要的类型和装饰器
|
||||
- 导入 `CertInfo`, `CertReader` 等证书相关类型
|
||||
- 导入 `createCertDomainGetterInputDefine`, `createRemoteSelectInputDefine` 等工具函数
|
||||
- 导入 `optionsUtils` 等辅助工具
|
||||
- 导入 `CertApplyPluginNames` 等常量
|
||||
|
||||
2. **使用 @IsTaskPlugin 注解注册插件**
|
||||
|
||||
- 配置插件的唯一标识、标题、图标
|
||||
- 设置插件分组
|
||||
- 配置默认策略(如 `SkipWhenSucceed`)
|
||||
- 确保类名与插件名称一致
|
||||
|
||||
3. **定义任务输入参数**
|
||||
|
||||
- 使用 `@TaskInput` 注解定义各种输入参数
|
||||
- 必须包含证书选择参数,用于获取前置任务输出的域名证书
|
||||
- 可以添加授权选择框、文本输入、选择框等参数
|
||||
- 使用 `createCertDomainGetterInputDefine` 获取证书域名列表
|
||||
|
||||
4. **实现动态显隐配置**
|
||||
|
||||
- 使用 `mergeScript` 实现根据其他输入值动态控制输入项的显隐状态
|
||||
- 利用 `ctx.compute` 函数访问表单中的其他字段值
|
||||
|
||||
5. **实现插件方法**
|
||||
|
||||
- **onInstance 方法**:插件实例化时执行的初始化操作
|
||||
- **execute 方法**:插件的核心执行逻辑
|
||||
- 获取授权信息
|
||||
- 读取证书信息
|
||||
- 执行具体的部署逻辑
|
||||
- 处理错误并记录日志
|
||||
- 实现幂等性:确保重复执行不会导致问题
|
||||
- 实现超时处理:设置合理的超时时间
|
||||
- 实现回滚机制:在部署失败时能够回滚到之前的状态
|
||||
- **后端获取选项方法**:用于前端从后端获取选项的方法
|
||||
|
||||
6. **遵循开发最佳实践**
|
||||
- 插件命名:遵循命名规范,大写字母开头,驼峰命名
|
||||
- 类名规范:类名应与插件名称一致
|
||||
- 日志输出:使用 `this.logger` 输出日志
|
||||
- 错误处理:捕获并记录执行过程中的错误
|
||||
- 授权获取:使用 `this.getAccess(accessId)` 获取授权信息
|
||||
|
||||
## 输出规范
|
||||
|
||||
- 必须包含完整的插件实现代码
|
||||
- 代码必须包含详细的注释说明
|
||||
- 提供完整的示例代码,展示插件的使用方法
|
||||
- 包含开发注意事项和最佳实践
|
||||
|
||||
## 示例
|
||||
|
||||
### 示例 1: 基本 Task 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个 Task 插件,用于将证书部署到对应的应用上。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
|
||||
import { optionsUtils } from '@certd/basic';
|
||||
import { CertApplyPluginNames } from '@certd/plugin-cert';
|
||||
|
||||
@IsTaskPlugin({
|
||||
//命名规范,插件类型+功能,大写字母开头,驼峰命名
|
||||
name: 'DemoTest',
|
||||
title: 'Demo-测试插件',
|
||||
icon: 'clarity:plugin-line',
|
||||
//插件分组
|
||||
group: pluginGroups.other.key,
|
||||
default: {
|
||||
//默认值配置照抄即可
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
//类名规范,跟上面插件名称(name)一致
|
||||
export class DemoTest extends AbstractTaskPlugin {
|
||||
//测试参数
|
||||
@TaskInput({
|
||||
title: '属性示例',
|
||||
value: '默认值',
|
||||
component: {
|
||||
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn
|
||||
name: 'a-input',
|
||||
vModel: 'value', //双向绑定组件的props名称
|
||||
},
|
||||
helper: '帮助说明,[链接](https://certd.docmirror.cn)',
|
||||
required: false, //是否必填
|
||||
})
|
||||
text!: string;
|
||||
|
||||
//测试参数
|
||||
@TaskInput({
|
||||
title: '选择框',
|
||||
component: {
|
||||
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/select-cn
|
||||
name: 'a-auto-complete',
|
||||
vModel: 'value',
|
||||
options: [
|
||||
//选项列表
|
||||
{ label: '动态显', value: 'show' },
|
||||
{ label: '动态隐', value: 'hide' },
|
||||
],
|
||||
},
|
||||
})
|
||||
select!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '动态显隐',
|
||||
helper: '我会根据选择框的值进行显隐',
|
||||
show: true, //动态计算的值会覆盖它
|
||||
//动态计算脚本, mergeScript返回的对象会合并当前配置
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.select === 'show';
|
||||
})
|
||||
}
|
||||
`,
|
||||
})
|
||||
showText!: string;
|
||||
|
||||
//测试参数
|
||||
@TaskInput({
|
||||
title: '多选框',
|
||||
component: {
|
||||
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/select-cn
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
mode: 'tags',
|
||||
multiple: true,
|
||||
options: [
|
||||
{ value: '1', label: '选项1' },
|
||||
{ value: '2', label: '选项2' },
|
||||
],
|
||||
},
|
||||
})
|
||||
multiSelect!: string;
|
||||
|
||||
//测试参数
|
||||
@TaskInput({
|
||||
title: 'switch',
|
||||
component: {
|
||||
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/switch-cn
|
||||
name: 'a-switch',
|
||||
vModel: 'checked',
|
||||
},
|
||||
})
|
||||
switch!: boolean;
|
||||
|
||||
//证书选择,此项必须要有
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
helper: '请选择前置任务输出的域名证书',
|
||||
component: {
|
||||
name: 'output-selector',
|
||||
from: [...CertApplyPluginNames],
|
||||
},
|
||||
// required: true, // 必填
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||
//前端可以展示,当前申请的证书域名列表
|
||||
certDomains!: string[];
|
||||
|
||||
//授权选择框
|
||||
@TaskInput({
|
||||
title: 'demo授权',
|
||||
helper: 'demoAccess授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
type: 'demo', //固定授权类型
|
||||
},
|
||||
// rules: [{ required: true, message: '此项必填' }],
|
||||
// required: true, //必填
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@TaskInput(
|
||||
createRemoteSelectInputDefine({
|
||||
title: '从后端获取选项',
|
||||
helper: '选择时可以从后端获取选项',
|
||||
action: DemoTest.prototype.onGetSiteList.name,
|
||||
//当以下参数变化时,触发获取选项
|
||||
watches: ['certDomains', 'accessId'],
|
||||
required: true,
|
||||
single: false, // 是否单选
|
||||
pager: true, // 是否卡其分页查询
|
||||
search: true, // 是否开启搜索
|
||||
})
|
||||
)
|
||||
siteName!: string | string[];
|
||||
|
||||
//插件实例化时执行的方法
|
||||
async onInstance() {}
|
||||
|
||||
//插件执行方法
|
||||
async execute(): Promise<void> {
|
||||
const { select, text, cert, accessId } = this;
|
||||
|
||||
try {
|
||||
const access = await this.getAccess(accessId);
|
||||
this.logger.debug('access', access);
|
||||
} catch (e) {
|
||||
this.logger.error('获取授权失败', e);
|
||||
}
|
||||
|
||||
try {
|
||||
const certReader = new CertReader(cert);
|
||||
this.logger.debug('certReader', certReader);
|
||||
} catch (e) {
|
||||
this.logger.error('读取crt失败', e);
|
||||
}
|
||||
|
||||
this.logger.info('DemoTestPlugin execute');
|
||||
this.logger.info('text:', text);
|
||||
this.logger.info('select:', select);
|
||||
this.logger.info('switch:', this.switch);
|
||||
this.logger.info('授权id:', accessId);
|
||||
|
||||
// 具体的部署逻辑
|
||||
// const res = await this.http.request({
|
||||
// url: 'https://api.demo.com',
|
||||
// method: 'GET',
|
||||
// });
|
||||
// if (res.code !== 0) {
|
||||
// //检查res是否报错,你需要抛异常,来结束插件执行,否则会判定为执行成功,下次执行时会跳过本任务
|
||||
// throw new Error(res.message);
|
||||
// }
|
||||
// this.logger.info('部署成功:', res);
|
||||
}
|
||||
|
||||
//此方法演示,如何让前端在添加插件时可以从后端获取选项,这里是后端返回选项的方法
|
||||
async onGetSiteList(req: PageSearch) {
|
||||
if (!this.accessId) {
|
||||
throw new Error('请选择Access授权');
|
||||
}
|
||||
|
||||
const pager = new Pager(req);
|
||||
|
||||
// @ts-ignore
|
||||
const access = await this.getAccess(this.accessId);
|
||||
//
|
||||
// 根据接口情况是否支持翻页查询,和关键字查询, 传递对应的参数,pager.pageNo,pager.pageSize, req.searchKey
|
||||
// const siteRes = await access.GetDomainList(req);
|
||||
//以下是模拟数据
|
||||
const siteRes = [
|
||||
{ id: 1, siteName: 'site1.com' },
|
||||
{ id: 2, siteName: 'site2.com' },
|
||||
{ id: 3, siteName: 'site2.com' },
|
||||
];
|
||||
//转换为前端所需要的格式
|
||||
const options = siteRes.map((item: any) => {
|
||||
return {
|
||||
value: item.siteName,
|
||||
label: item.siteName,
|
||||
domain: item.siteName, //这里必须要包含domain 否则后面分组时候全部分配到未匹配中
|
||||
};
|
||||
});
|
||||
//将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
|
||||
return {
|
||||
list: optionsUtils.buildGroupOptions(options, this.certDomains), //分组后的列表
|
||||
total: siteRes.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例 2: Nginx 部署 Task 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个 Task 插件,用于将证书部署到 Nginx 服务器上。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AbstractTaskPlugin, CertInfo, IsTaskPlugin, TaskInput, pluginGroups, RunStrategy } from '@certd/pipeline';
|
||||
import { CertReader } from '@certd/plugin-lib';
|
||||
|
||||
/**
|
||||
* Nginx 证书部署插件
|
||||
*/
|
||||
@IsTaskPlugin({
|
||||
name: 'NginxDeploy',
|
||||
title: 'Nginx 部署',
|
||||
desc: '将证书部署到 Nginx 服务器上',
|
||||
icon: 'clarity:server-line',
|
||||
group: pluginGroups.deploy.key,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
export class NginxDeploy extends AbstractTaskPlugin {
|
||||
/**
|
||||
* 服务器授权
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '服务器授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
vModel: 'accessId',
|
||||
accessTypes: ['ssh'],
|
||||
placeholder: '请选择服务器授权',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
accessId = '';
|
||||
|
||||
/**
|
||||
* 域名证书
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
component: {
|
||||
name: 'output-selector',
|
||||
from: ['CertApply', 'CertApplyCloudflare'],
|
||||
field: 'cert',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
/**
|
||||
* 证书路径
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '证书路径',
|
||||
value: '/etc/nginx/ssl',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: '请输入证书存储路径',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
certPath = '';
|
||||
|
||||
/**
|
||||
* Nginx 配置文件路径
|
||||
*/
|
||||
@TaskInput({
|
||||
title: 'Nginx 配置文件',
|
||||
value: '/etc/nginx/conf.d',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: '请输入 Nginx 配置文件路径',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
nginxConfPath = '';
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '服务名称',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: '请输入服务名称(用于生成配置文件)',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
serviceName = '';
|
||||
|
||||
/**
|
||||
* 执行部署
|
||||
*/
|
||||
async execute(): Promise<void> {
|
||||
this.logger.info('开始部署证书到 Nginx');
|
||||
|
||||
try {
|
||||
// 1. 获取服务器授权
|
||||
const sshAccess = await this.getAccess(this.accessId);
|
||||
this.logger.info('获取服务器授权成功');
|
||||
|
||||
// 2. 读取证书信息
|
||||
const certReader = new CertReader(this.cert);
|
||||
const cert = certReader.getCert();
|
||||
const key = certReader.getKey();
|
||||
const fullchain = certReader.getFullChain();
|
||||
this.logger.info('读取证书信息成功');
|
||||
|
||||
// 3. 准备部署路径
|
||||
const certFile = `${this.certPath}/${this.serviceName}.pem`;
|
||||
const keyFile = `${this.certPath}/${this.serviceName}.key`;
|
||||
const confFile = `${this.nginxConfPath}/${this.serviceName}.conf`;
|
||||
|
||||
// 4. 创建证书目录
|
||||
await sshAccess.exec(`mkdir -p ${this.certPath}`);
|
||||
this.logger.info('创建证书目录成功');
|
||||
|
||||
// 5. 上传证书文件
|
||||
await sshAccess.uploadContent(cert, certFile);
|
||||
await sshAccess.uploadContent(key, keyFile);
|
||||
await sshAccess.uploadContent(fullchain, `${this.certPath}/${this.serviceName}-fullchain.pem`);
|
||||
this.logger.info('上传证书文件成功');
|
||||
|
||||
// 6. 生成 Nginx 配置
|
||||
const nginxConf = `server {
|
||||
listen 443 ssl;
|
||||
server_name ${this.cert.domains.join(' ')};
|
||||
|
||||
ssl_certificate ${certFile};
|
||||
ssl_certificate_key ${keyFile};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// 7. 上传 Nginx 配置
|
||||
await sshAccess.uploadContent(nginxConf, confFile);
|
||||
this.logger.info('上传 Nginx 配置成功');
|
||||
|
||||
// 8. 测试 Nginx 配置
|
||||
const testResult = await sshAccess.exec('nginx -t');
|
||||
if (testResult.includes('test is successful')) {
|
||||
this.logger.info('Nginx 配置测试成功');
|
||||
} else {
|
||||
throw new Error(`Nginx 配置测试失败: ${testResult}`);
|
||||
}
|
||||
|
||||
// 9. 重启 Nginx 服务
|
||||
await sshAccess.exec('systemctl reload nginx');
|
||||
this.logger.info('重启 Nginx 服务成功');
|
||||
|
||||
this.logger.info('证书部署到 Nginx 成功');
|
||||
} catch (error) {
|
||||
this.logger.error('部署失败:', error);
|
||||
throw new Error(`部署到 Nginx 失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
new NginxDeploy();
|
||||
```
|
||||
|
||||
### 示例 3: 阿里云 OSS 部署 Task 插件
|
||||
|
||||
#### 用户输入
|
||||
|
||||
创建一个 Task 插件,用于将证书部署到阿里云 OSS 上。
|
||||
|
||||
#### 你的回答
|
||||
|
||||
```typescript
|
||||
import { AbstractTaskPlugin, CertInfo, IsTaskPlugin, TaskInput, pluginGroups, RunStrategy } from '@certd/pipeline';
|
||||
import { CertReader } from '@certd/plugin-lib';
|
||||
|
||||
/**
|
||||
* 阿里云 OSS 证书部署插件
|
||||
*/
|
||||
@IsTaskPlugin({
|
||||
name: 'AliyunOSSDeploy',
|
||||
title: '阿里云 OSS 部署',
|
||||
desc: '将证书部署到阿里云 OSS 存储上',
|
||||
icon: 'clarity:cloud-line',
|
||||
group: pluginGroups.deploy.key,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
export class AliyunOSSDeploy extends AbstractTaskPlugin {
|
||||
/**
|
||||
* 阿里云授权
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '阿里云授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
vModel: 'accessId',
|
||||
accessTypes: ['aliyun'],
|
||||
placeholder: '请选择阿里云授权',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
accessId = '';
|
||||
|
||||
/**
|
||||
* 域名证书
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
component: {
|
||||
name: 'output-selector',
|
||||
from: ['CertApply', 'CertApplyCloudflare'],
|
||||
field: 'cert',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
/**
|
||||
* OSS 存储桶
|
||||
*/
|
||||
@TaskInput({
|
||||
title: 'OSS 存储桶',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: '请输入 OSS 存储桶名称',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
bucketName = '';
|
||||
|
||||
/**
|
||||
* 存储路径
|
||||
*/
|
||||
@TaskInput({
|
||||
title: '存储路径',
|
||||
value: 'ssl/',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
placeholder: '请输入证书存储路径',
|
||||
},
|
||||
})
|
||||
storagePath = '';
|
||||
|
||||
/**
|
||||
* 执行部署
|
||||
*/
|
||||
async execute(): Promise<void> {
|
||||
this.logger.info('开始部署证书到阿里云 OSS');
|
||||
|
||||
try {
|
||||
// 1. 获取阿里云授权
|
||||
const aliyunAccess = await this.getAccess(this.accessId);
|
||||
this.logger.info('获取阿里云授权成功');
|
||||
|
||||
// 2. 读取证书信息
|
||||
const certReader = new CertReader(this.cert);
|
||||
const cert = certReader.getCert();
|
||||
const key = certReader.getKey();
|
||||
const fullchain = certReader.getFullChain();
|
||||
this.logger.info('读取证书信息成功');
|
||||
|
||||
// 3. 准备存储路径
|
||||
const basePath = this.storagePath.endsWith('/') ? this.storagePath : `${this.storagePath}/`;
|
||||
const certFileName = `${basePath}${this.cert.domains[0]}.pem`;
|
||||
const keyFileName = `${basePath}${this.cert.domains[0]}.key`;
|
||||
const fullchainFileName = `${basePath}${this.cert.domains[0]}-fullchain.pem`;
|
||||
|
||||
// 4. 上传证书到 OSS
|
||||
await aliyunAccess.uploadToOSS({
|
||||
bucket: this.bucketName,
|
||||
key: certFileName,
|
||||
content: cert,
|
||||
});
|
||||
this.logger.info('上传证书文件成功');
|
||||
|
||||
await aliyunAccess.uploadToOSS({
|
||||
bucket: this.bucketName,
|
||||
key: keyFileName,
|
||||
content: key,
|
||||
});
|
||||
this.logger.info('上传私钥文件成功');
|
||||
|
||||
await aliyunAccess.uploadToOSS({
|
||||
bucket: this.bucketName,
|
||||
key: fullchainFileName,
|
||||
content: fullchain,
|
||||
});
|
||||
this.logger.info('上传完整证书链成功');
|
||||
|
||||
// 5. 设置文件访问权限(可选)
|
||||
await aliyunAccess.setOSSObjectAcl({
|
||||
bucket: this.bucketName,
|
||||
key: certFileName,
|
||||
acl: 'private',
|
||||
});
|
||||
|
||||
await aliyunAccess.setOSSObjectAcl({
|
||||
bucket: this.bucketName,
|
||||
key: keyFileName,
|
||||
acl: 'private',
|
||||
});
|
||||
|
||||
await aliyunAccess.setOSSObjectAcl({
|
||||
bucket: this.bucketName,
|
||||
key: fullchainFileName,
|
||||
acl: 'private',
|
||||
});
|
||||
this.logger.info('设置文件访问权限成功');
|
||||
|
||||
this.logger.info('证书部署到阿里云 OSS 成功');
|
||||
} catch (error) {
|
||||
this.logger.error('部署失败:', error);
|
||||
throw new Error(`部署到阿里云 OSS 失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
new AliyunOSSDeploy();
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应遵循命名规范,大写字母开头,驼峰命名。
|
||||
2. **类名规范**:类名应与插件名称(name)一致。
|
||||
3. **证书选择**:必须包含证书选择参数,用于获取前置任务输出的域名证书。
|
||||
4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`,参数文本化,不要传对象,否则会输出`[object Object]}`。
|
||||
5. **错误处理**:执行过程中的错误应被捕获并记录。
|
||||
6. **授权获取**:使用 `this.getAccess(accessId)` 获取授权信息。
|
||||
7. **旧版数据兼容**: 新增@TaskInput注解的插件参数,必须要考虑旧版数据兼容,比如新增一个deployType参数,有两种值:`default`和`custom`,需要在使用时判空,走旧版逻辑。
|
||||
|
||||
## 部署逻辑注意事项
|
||||
|
||||
1. **部署接口逻辑**:
|
||||
|
||||
- 研究应用的部署接口逻辑,一般有两种:
|
||||
a. 用户选择网站ID,给网站部署新证书
|
||||
b. 用户选择证书ID,只需要更新证书即可
|
||||
- 保证多次执行都能针对同一个对象部署证书
|
||||
- 确保出错后重新运行能够回归到正常状态
|
||||
|
||||
2. **前置证书选择**:
|
||||
|
||||
- 前置证书可以是原始的 `certInfo` 类型,也可能是上传到平台之后返回的证书id
|
||||
- 根据接口要求选择合适的证书类型:
|
||||
a. 如果接口需要上传后的证书id,那么部署时要先将证书上传,再部署
|
||||
b. 如果接口需要原始的 `certInfo` 类型,那么直接使用 `certInfo` 部署证书
|
||||
c. 当两者都支持时,判断用户选择的证书类型,再考虑优先上传再部署
|
||||
|
||||
3. **证书清理**:
|
||||
- 如果是先上传再部署的,那么在部署完成后,可能需要考虑清理证书
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
我需要开发一个 Task 插件,用于将申请的证书部署到指定的应用系统中。请指导我如何实现。
|
||||
@@ -0,0 +1,129 @@
|
||||
# Task 插件开发指南
|
||||
|
||||
## 开发步骤
|
||||
|
||||
### 1. 导入必要的依赖
|
||||
|
||||
```typescript
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
|
||||
import { optionsUtils } from '@certd/basic';
|
||||
import { CertApplyPluginNames } from '@certd/plugin-cert';
|
||||
```
|
||||
|
||||
### 2. 使用 @IsTaskPlugin 注解注册插件
|
||||
|
||||
```typescript
|
||||
@IsTaskPlugin({
|
||||
// 命名规范,插件类型+功能,大写字母开头,驼峰命名
|
||||
name: 'DemoTest',
|
||||
title: 'Demo-测试插件', // 插件标题
|
||||
icon: 'clarity:plugin-line', // 插件图标
|
||||
// 插件分组
|
||||
group: pluginGroups.other.key,
|
||||
default: {
|
||||
// 默认值配置照抄即可
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
// 类名规范,跟上面插件名称(name)一致
|
||||
export class DemoTest extends AbstractTaskPlugin {
|
||||
// 插件实现...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 定义任务输入参数
|
||||
|
||||
使用 `@TaskInput` 注解定义任务输入参数:
|
||||
|
||||
```typescript
|
||||
// 测试参数
|
||||
@TaskInput({
|
||||
title: '属性示例',
|
||||
value: '默认值',
|
||||
component: {
|
||||
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn
|
||||
name: 'a-input',
|
||||
vModel: 'value', //双向绑定组件的props名称
|
||||
},
|
||||
helper: '帮助说明,[链接](https://certd.docmirror.cn)',
|
||||
required: false, //是否必填
|
||||
})
|
||||
text!: string;
|
||||
|
||||
//证书选择,此项必须要有
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
helper: '请选择前置任务输出的域名证书',
|
||||
component: {
|
||||
name: 'output-selector',
|
||||
from: [...CertApplyPluginNames],
|
||||
},
|
||||
// required: true, // 必填
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||
//前端可以展示,当前申请的证书域名列表
|
||||
certDomains!: string[];
|
||||
|
||||
//授权选择框
|
||||
@TaskInput({
|
||||
title: 'demo授权',
|
||||
helper: 'demoAccess授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
type: 'demo', //固定授权类型
|
||||
},
|
||||
// rules: [{ required: true, message: '此项必填' }],
|
||||
// required: true, //必填
|
||||
})
|
||||
accessId!: string;
|
||||
```
|
||||
|
||||
### 4. 实现插件方法
|
||||
|
||||
```typescript
|
||||
//插件实例化时执行的方法
|
||||
async onInstance() {}
|
||||
|
||||
//插件执行方法
|
||||
async execute(): Promise<void> {
|
||||
const { select, text, cert, accessId } = this;
|
||||
|
||||
try {
|
||||
const access = await this.getAccess(accessId);
|
||||
this.logger.debug('access', access);
|
||||
} catch (e) {
|
||||
this.logger.error('获取授权失败', e);
|
||||
}
|
||||
|
||||
try {
|
||||
const certReader = new CertReader(cert);
|
||||
this.logger.debug('certReader', certReader);
|
||||
} catch (e) {
|
||||
this.logger.error('读取crt失败', e);
|
||||
}
|
||||
|
||||
this.logger.info('DemoTestPlugin execute');
|
||||
this.logger.info('text:', text);
|
||||
this.logger.info('select:', select);
|
||||
this.logger.info('switch:', this.switch);
|
||||
this.logger.info('授权id:', accessId);
|
||||
|
||||
// 具体的部署逻辑
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应遵循命名规范,大写字母开头,驼峰命名。
|
||||
2. **类名规范**:类名应与插件名称(name)一致。
|
||||
3. **证书选择**:必须包含证书选择参数,用于获取前置任务输出的域名证书。
|
||||
4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。
|
||||
5. **错误处理**:执行过程中的错误应被捕获并记录。
|
||||
6. **授权获取**:使用 `this.getAccess(accessId)` 获取授权信息。
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "client",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-client",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-mysql",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev-mysql"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-pg",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev-pg"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-pgpl",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev-pgpl"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-common",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev-commpro"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-new",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": ["dev-new"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"name": "server-local-plus",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev-localplus"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"env": {
|
||||
"plus_use_prod": "false",
|
||||
"PLUS_SERVER_BASE_URL": "http://127.0.0.1:11007"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server-local-plus2",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev-localplus2"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"env": {
|
||||
"plus_use_prod": "false",
|
||||
"PLUS_SERVER_BASE_URL": "http://127.0.0.1:11007"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server-local-comm",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": ["run", "dev-localcomm"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"env": {
|
||||
"plus_use_prod": "false",
|
||||
"PLUS_SERVER_BASE_URL": "http://127.0.0.1:11007"
|
||||
}
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "all",
|
||||
"configurations": ["server", "client"],
|
||||
"stopAll": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"eslint.debug": false,
|
||||
"eslint.format.enable": true,
|
||||
"typescript.tsc.autoDetect": "watch",
|
||||
"git.scanRepositories": ["./packages/pro"],
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"explorer.autoReveal": false,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "vscode.css-language-features"
|
||||
},
|
||||
"scm.repositories.visible": 9,
|
||||
"scm.repositories.explorer": false,
|
||||
"scm.repositories.selectionMode": "multiple",
|
||||
"scm.repositories.sortOrder": "discovery time",
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "启动Client",
|
||||
"type": "shell",
|
||||
"command": "npm",
|
||||
"args": ["run", "dev"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-client"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "启动Server",
|
||||
"type": "shell",
|
||||
"command": "npm",
|
||||
"args": ["run", "dev"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/packages/ui/certd-server"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "同时启动Client和Server",
|
||||
"dependsOn": ["启动Client", "启动Server"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
# Certd 开发 Agent 上下文
|
||||
|
||||
进入仓库后先读本文。本文同时包含常驻规则、仓库地图、常用入口和验证命令;不要依赖分散规则文件。
|
||||
思考时也要使用中文。
|
||||
|
||||
## 项目定位
|
||||
|
||||
Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。
|
||||
|
||||
核心模型是“证书流水线”:
|
||||
|
||||
- 通过 ACME 申请证书。
|
||||
- 完成 DNS-01、HTTP-01、CNAME 代理或服务商验证。
|
||||
- 导出 pem、pfx、der、jks、p7b 等证书格式。
|
||||
- 部署到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标。
|
||||
- 通知用户,并监控站点证书过期时间。
|
||||
|
||||
系统会保存证书、云厂商凭据、SSH 信息、API Key 等敏感数据。始终按私有化/本地部署产品处理,避免泄露本地数据和配置。
|
||||
|
||||
## 仓库边界
|
||||
|
||||
- 根仓库是 pnpm + lerna monorepo;不要引入 npm/yarn lockfile。
|
||||
- `packages/pro/` 是独立 Git 工作区;修改商业版代码后,必须在 `packages/pro` 内单独执行 `git status` / `git diff`。
|
||||
- 不要把 `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等运行时或构建产物纳入改动,除非任务明确要求。
|
||||
- 例外:分析插件动态依赖时,可以只读查看后端数据目录下的 `./data/.runtime-deps`;该目录用于 runtime-deps 动态安装第三方 SDK,不应纳入提交。
|
||||
|
||||
核心包:
|
||||
|
||||
- `packages/ui/certd-server`:后端服务。
|
||||
- `packages/ui/certd-client`:前端 Web 管理台。
|
||||
- `packages/core/pipeline`:流水线核心。
|
||||
- `packages/core/acme-client`:ACME 客户端。
|
||||
- `packages/plugins/plugin-lib`:插件共享能力。
|
||||
|
||||
## 仓库地图
|
||||
|
||||
- `package.json`:根脚本与 workspace 元信息。
|
||||
- `pnpm-workspace.yaml`:workspace 匹配规则,包含 `packages/**`、`packages/ui/**`。
|
||||
- `lerna.json`:lerna-lite 配置。
|
||||
- `docs`:VitePress 文档站。
|
||||
- `docker`:Docker 安装和运行相关文件。
|
||||
- `packages/core/acme-client`:ACME 协议客户端。
|
||||
- `packages/core/basic`:共享基础工具。
|
||||
- `packages/core/pipeline`:流水线核心抽象、插件模型、执行上下文。
|
||||
- `packages/libs`:共享集成库。
|
||||
- `packages/plugins/plugin-lib`:证书、DNS Provider、格式转换等插件共享能力。
|
||||
- `packages/plugins/plugin-cert`:证书插件包入口。
|
||||
- `packages/ui/certd-server`:后端 Midway 服务。
|
||||
- `packages/ui/certd-client`:前端 Vue/Vite 管理台。
|
||||
- `packages/pro`:商业版独立 Git 工作区,需在该目录内单独检查状态。
|
||||
|
||||
常见运行时或生成产物:
|
||||
|
||||
- 根目录:`node_modules`、`logs`、`output`、`lerna-debug.log`、`tmp-certd-client-vite*.log`。
|
||||
- 后端:`packages/ui/certd-server/data`、`packages/ui/certd-server/logs`。
|
||||
- 后端动态依赖:`./data/.runtime-deps`,常见于阿里云 SDK、腾讯云 SDK 等插件第三方依赖。
|
||||
- 各包:`dist`。
|
||||
- 插件:metadata/yaml 导出结果。
|
||||
|
||||
## 常用验证
|
||||
|
||||
- 前端改动文件格式化:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`。
|
||||
- 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`。
|
||||
- 后端单元测试:`cd packages\ui\certd-server && npm run unit`。
|
||||
- 后端改动文件 lint fix:`cd packages\ui\certd-server && npm run lint`(该脚本即 `mwts fix`,对改动文件执行 ESLint 自动修复)。
|
||||
- 其他package lint fix:`cd packages\xxx\xxxx && npm run lint`。
|
||||
|
||||
## 通用工作规则
|
||||
|
||||
- 先读本文,再按任务读取具体代码或技能文件。
|
||||
- 与用户沟通、代码注释、文档、日志和面向用户的界面文案,尽量使用中文;技术专有名词、代码标识符、第三方库名称和命令可保留英文。
|
||||
- PowerShell 读取中文、Markdown、locale、文档类文件时使用 `Get-Content -Raw -Encoding UTF8`;仍乱码时先执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()`。
|
||||
- PowerShell 中用 `rg` 搜索含引号、括号、反斜杠的 pattern 时,优先用单引号包裹整个 pattern,例如 `rg 'await import\("tencentcloud-sdk-nodejs' packages/ui/certd-server/src -g '*.ts'`。
|
||||
- 手工编辑或创建文件时优先使用 `apply_patch`。单个文件内有多处不连续改动时,拆成多个独立的 `*** Update File` 块,每块只改一处附近上下文;不要在同一个 update hunk 里强塞多个 `@@`。
|
||||
- 只有真正机械化的大批量替换、格式化或生成任务才考虑脚本/工具。若必须使用临时脚本,应放在临时目录并在同一个受控步骤内完成创建、执行、删除;不要把临时脚本落在仓库里跨多步工具调用执行。
|
||||
- 不要主动运行 `pnpm install`;缺依赖、TTY、网络导致安装或测试失败时,停止尝试并说明环境问题。
|
||||
- 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。
|
||||
- 代码可读性优先于短写法。尽量少用三元表达式,优先使用完整的 `if/else`;复杂条件、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
|
||||
- 方法调用链不要直接塞进另一个方法参数;先用有意义的局部变量承接返回值,再传入下一步。
|
||||
- 不要在单一表达式内嵌套分支、对象构造与方法调用。优先使用清晰的 `if/else` 分支;仅在确实能降低复杂度时才提取有意义的中间变量,避免为拆分而增加阅读跳转。
|
||||
- 注释优先使用中文,尤其是业务规则、兼容逻辑、协议细节和隐藏风险;文件已有英文风格或引用外部术语时可保持一致。
|
||||
- 遵守 DRY 和单一职责;第三次出现的业务规则、字段转换、权限判断、Repository 选择、事务传播、金额计算等逻辑,应优先抽成合适 helper 或 service 方法。
|
||||
- 如无明确复用价值,不要随意新增 `normalizeXxx` 等辅助方法;优先在现有业务方法中直接处理简单兼容或字段转换逻辑,避免增加不必要的抽象层。
|
||||
|
||||
|
||||
## 测试与验证
|
||||
|
||||
- 新功能、缺陷修复和重构遵循 TDD:先编写或更新测试,覆盖正常路径、边界条件和明确的错误处理;先运行测试确认 RED,再实现最小改动,最后运行聚焦测试确认 GREEN。
|
||||
- 单元测试应优先直接测试原始业务方法,不要为了方便测试而抽取没有业务价值的 helper;数据库、RPC 和其他外部依赖可以使用 mock 隔离。
|
||||
- 确实不适合先写测试时,在回复中说明原因和替代验证方式。
|
||||
- 后补单元测试时,按正确行为写预期;若红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。
|
||||
- 后端纯单测放在 `src/**/*.test.ts`,尽量与被测文件相邻;`test:unit` 只跑这些文件,构建/打包应排除 `*.test.ts`。
|
||||
- 单测需要 mock ESM 静态 import(如 `@certd/plus-core` 的 `isPlus`、第三方包)时,**优先使用 `esmock`**,通过 `esmock(被测模块路径, { 依赖模块: { 导出: mock实现 } })` 加载被测模块并替换依赖;**不要为了测试给业务代码加注入点/改业务结构**(如不需要给类加 `isPlusFn` 之类的可注入属性)。
|
||||
- 各包 `test:unit` 脚本应显式设置 `NODE_ENV=unittest`。
|
||||
- 单包单测优先用 `cd <包目录> && npm run test:unit`,例如 `cd packages\ui\certd-server && npm run test:unit`。
|
||||
- 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。
|
||||
|
||||
## 规范回顾
|
||||
|
||||
- 每次任务收尾时,回顾用户提出的要求,识别其中可跨任务复用、稳定且不与既有规则冲突的约束。
|
||||
- 只将经过确认的共性要求精炼后写入本文;一次性任务细节、临时偏好和未经确认的推断不要写入长期规范。
|
||||
- 更新本文后检查规则是否清晰、可执行,并删除重复或互相矛盾的表述。
|
||||
|
||||
|
||||
## 后端规则
|
||||
|
||||
- 后端主包是 `packages/ui/certd-server`,使用 Node.js、ESM、TypeScript、MidwayJS 3、Koa、TypeORM 和 SQL 迁移。
|
||||
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
|
||||
- 表结构变更必须添加或更新 `packages/ui/certd-server/db/migration/*.sql`;不要依赖 TypeORM 自动同步。
|
||||
- 文件上传接口 `/basic/file/upload` 返回临时 key;业务保存前必须调用 `FileService.saveFile(userId, key, "public" | "private")` 转成永久 key,不能直接保存 `tmpfile_key_...`。
|
||||
- 方法参数超过 3 个时,优先改为对象参数。
|
||||
- 事务链路方法统一用 `method(ctx, req)`,`ctx` 放第一位并承载 `manager?: EntityManager`,业务参数放 `req` 对象。
|
||||
- 只有需要事务传播时才定义 `ctx`;普通查询、纯函数和简单私有方法继续使用明确参数。
|
||||
- 需要按事务上下文取 Repository 时,用 `BaseService.getRepo(ctx, EntityClass)`。
|
||||
- 需要“有事务则复用、无事务则开启”时,用 `BaseService.transactionWithCtx(ctx, callback)`。
|
||||
- 基础 CRUD 数据访问优先复用 `BaseService` 的 `find`、`findOne`、`list`、`page`、`update`、`deleteWhere` 等方法;不要从 `repository.createQueryBuilder()` 开始重复实现完整查询或更新。仅将关键词组合筛选、联表、业务排序等基类无法表达的部分放入 `list/page` 的 `buildQuery`。
|
||||
- 拼接可选 `projectId` 查询条件时,**必须**使用 `BaseService.buildUserProjectQuery(userId, projectId)`,禁止直接写 `{ userId, projectId }`。因为 `projectId` 可能为 `null`/`undefined`,直接放入查询会生成错误的 `WHERE projectId = NULL` 条件。
|
||||
- `ctx` 类型复用 `BaseService` 导出的 `ServiceContext`。
|
||||
- 新增 service 方法避免与 `BaseService` 方法签名冲突,例如不要用 `delete(id)` 覆盖 `delete(ids, where?)`;改用 `deleteById` 等具体名称。
|
||||
|
||||
### 证书仓库(cd_cert_info)空证书记录【必须保留】
|
||||
|
||||
- 保存流水线时(`PipelineService.save`)会调用 `CertInfoService.updateDomains` 同步该流水线的 active 证书记录(占位记录,`certInfo` 为空)。**这些记录必须保留,不要删除**:开放接口(OpenAPI `autoApply`)触发申请证书前,会先查询证书仓库中是否有该流水线的记录——有记录(即使证书内容为空)说明该流水线已存在、证书正在申请中,直接复用/触发已有流水线;若删掉这些记录,在证书申请成功前的空窗期内,开放接口每次调用都会重复创建新流水线。
|
||||
- 同步规则(`updateDomains`,只处理 active 记录,inactive/revoked 历史保留):① 流水线中每个证书申请任务(CertApply 类步骤)对应仓库一条 active 记录(按 `task_id` 关联),没有则创建空证书记录;② 删除孤儿 active 记录(流水线中已不存在对应任务 id 的,含 `task_id` 为空的历史遗留);③ 一个流水线多个申请任务时维护多条记录,随时保持一致。
|
||||
- `updateCertByPipelineId`(申请证书成功后写入仓库)中同样**不得删除**该流水线的空证书记录;新证书总是新建一条 active 记录,并按相同 `task_id` 把同流水线其他 active 记录标记为 inactive,避免同一流水线出现多条 active。
|
||||
- 吊销旧证书(`CertRevokeOld` 后置任务)按证书申请任务 id(`task_id`)精确匹配:只吊销同一申请任务产出的 inactive 旧证书,不误伤其他任务仍在使用的证书。
|
||||
|
||||
### 后端地图
|
||||
|
||||
- `packages/ui/certd-server/bootstrap.js`:Midway 启动入口,使用 `@midwayjs/bootstrap`。
|
||||
- `packages/ui/certd-server/src/configuration.ts`:Midway 主配置,注册组件和全局中间件。
|
||||
- `packages/ui/certd-server/src/config/config.default.ts`:端口、HTTPS、静态文件、cron、TypeORM、Flyway、上传、JWT、Swagger 默认配置。
|
||||
- `packages/ui/certd-server/src/config/loader.ts`:读取 `.env`、`.env.<env>.yaml`,支持 `certd_` 前缀环境变量覆盖嵌套配置。
|
||||
- `packages/ui/certd-server/src/modules`:业务模块根目录,常见模块包括:
|
||||
- `basic`
|
||||
- `cert`
|
||||
- `cname`
|
||||
- `cron`
|
||||
- `login`
|
||||
- `monitor`
|
||||
- `open`
|
||||
- `pipeline`
|
||||
- `plugin`
|
||||
- `suite`
|
||||
- `sys`
|
||||
- `packages/ui/certd-server/src/controller`:API 入口,按 `basic`、`user`、`sys`、`openapi` 分组。
|
||||
- `packages/ui/certd-server/db/migration`:SQL 迁移目录;TypeORM `synchronize: false`,表结构变更必须配套迁移 SQL。
|
||||
- 后端测试使用 Mocha + Node `assert/strict`;纯单测放在 `src/**/*.test.ts`,可参考 `src/utils/random.test.ts`、`src/controller/basic/app-controller.test.ts`、`src/modules/pipeline/service/pipeline-service.test.ts`。
|
||||
|
||||
## 前端规则
|
||||
|
||||
- 前端主包是 `packages/ui/certd-client`,使用 Vue 3、Vite、TypeScript、Ant Design Vue、Fast Crud、Pinia、vue-router、vue-i18n。
|
||||
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`。
|
||||
- 前端用户提示**统一使用 `notification`**(ant-design-vue 的 `notification.success({ message: X })` / `.error` / `.warning` / `.info`),**不要使用 `message` API**;`src/vben/` 目录(vben 框架内置布局/组件)保持原样不动。
|
||||
- 不要运行前端 `pnpm tsc` / `vue-tsc`;当前 `vue-tsc@1.8.27` 会抛无效内部错误。前端 `test:unit` 只是占位脚本,也不要跑。
|
||||
- 前端 TS/Vue/locale 改动后,只对本次改动文件运行现有 Prettier / ESLint:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>` 和 `packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`。
|
||||
- 需要手写 render 的地方(如 `Modal.confirm` 的 `content`、`useFormDialog` 的 `body`、动态插槽等),一律用 JSX 编写,禁止使用 `h()` 创建 VNode:
|
||||
- `.vue` 组件:把 `<script lang="ts" setup>` 改为 `<script lang="tsx" setup>`,然后在 JSX 中直接写元素(如 `content: () => (<div class="xxx">...</div>)`)。
|
||||
- `.ts` 文件:直接把文件后缀改为 `.tsx`,再写 JSX render。
|
||||
- 列表管理、后台管理、记录查询、CRUD 表格页面优先使用 Fast Crud;开发或重构前读 `.trae/skills/fast-crud-page-dev/SKILL.md`。
|
||||
- 只有轻量只读展示、强交互自定义界面或既有页面模式明显不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明。
|
||||
- 内嵌 Fast Crud 时,外层必须有稳定高度或完整 `flex: 1; min-height: 0` 链路。
|
||||
- 前端组件样式统一写在 `<style>` / Less / CSS 文件里,通过样式名映射到元素;尽量不要在元素上直接写 `style`。
|
||||
- 每个组件都要有一个稳定的根样式名,并把组件下方样式全部包在该根样式名内;尽量不要使用 `scoped`。
|
||||
- 可复用的公共样式名放在 `packages/ui/certd-client/src/style` 下维护,优先使用 `cd-` 前缀,避免散落在业务组件里重复定义。
|
||||
- 后台管理列表展示或筛选用户字段时,优先参考 `packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx` 的 `userId` 字段模式,用 `table-select` + `/sys/authority/user/getSimpleUserByIds` 字典回显和搜索。
|
||||
- 对话框里只做确认可用 `Modal.confirm`;有字段输入、表单校验或提交字段时,必须用 `useFormDialog` / `openFormDialog`。
|
||||
- 使用 `table-select` / `fs-table-select` 时,`dict` 必须配置 `getNodesByValues`,支持根据已选 ID 批量懒加载选项,确保编辑回显和异步数据场景正常工作。
|
||||
|
||||
### 前端地图
|
||||
|
||||
- `packages/ui/certd-client/vite.config.ts`:Vite 配置。
|
||||
- dev 端口:`3008`
|
||||
- 代理路径:`/api`、`/certd/api`
|
||||
- 代理目标:`127.0.0.1:7001`
|
||||
- `packages/ui/certd-client/src/main.ts`:Vue 启动入口,注册 AntDV、Vben、router、全局组件、插件和偏好设置。
|
||||
- `packages/ui/certd-client/src/App.vue`:根组件,包含 `AConfigProvider`、`FsFormProvider`、`router-view`。
|
||||
- `packages/ui/certd-client/src/router/index.ts`、`src/router/resolve.ts`:路由入口,使用 `createWebHashHistory`。
|
||||
- `packages/ui/certd-client/src/router/source/modules/certd.ts`:Certd 主业务路由。
|
||||
- `packages/ui/certd-client/src/store`:Pinia store,主要包括:
|
||||
- `user`
|
||||
- `project`
|
||||
- `settings`
|
||||
- `plugin`
|
||||
- `packages/ui/certd-client/src/api/service.ts`:Axios 封装。
|
||||
- `packages/ui/certd-client/src/api/tools.ts`:错误与响应工具。
|
||||
- `packages/ui/certd-client/src/views/certd`:核心业务视图,常见目录包括:
|
||||
- `pipeline`
|
||||
- `cert`
|
||||
- `monitor`
|
||||
- `access`
|
||||
- `notification`
|
||||
- `open`
|
||||
- `project`
|
||||
- `suite`
|
||||
- `wallet`
|
||||
- `packages/ui/certd-client/src/locales`:国际化入口与语言包。
|
||||
- Fast Crud 页面常见拆分是 `api.ts`、`crud.tsx`、`index.vue`;可参考 `src/views/certd/access`、`src/views/sys/suite/user-suite/crud.tsx`、`src/views/certd/wallet/index.vue`。
|
||||
|
||||
## 流水线与插件规则
|
||||
|
||||
- 插件是核心能力。新增服务商、DNS 验证、证书部署、通知方式,通常放到插件包或 `packages/ui/certd-server/src/plugins/<plugin-name>/`。
|
||||
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib`。
|
||||
- 插件依赖的第三方 SDK 可能通过 runtime-deps 动态安装到后端运行目录 `./data/.runtime-deps`。分析阿里云、腾讯云等 SDK 行为时,需要进入该目录阅读实际安装版本代码。
|
||||
- 修改证书申请、验证、部署或通知行为时,先判断归属:ACME client、pipeline 核心、后端 module/service/entity/controller、具体插件、前端 view/form/schema。
|
||||
- 单个服务商或部署目标的问题,不要轻易修改共享 pipeline/core;只有可复用公共语义或跨插件一致行为才上移到 `packages/core/pipeline` 或 `packages/plugins/plugin-lib`。
|
||||
- `newAccount({ onlyReturnExisting: true })` 可用同一个 account private key 取回已创建账号 URL,且不会再次消费 EAB。
|
||||
- 修改 EAB `kid` 后,应重新生成绑定该 `kid` 的 account private key;否则应阻止继续申请并提示刷新账号私钥。
|
||||
- 插件开发前先读对应技能:`.trae/skills/dns-provider-dev/SKILL.md`、`.trae/skills/task-plugin-dev/SKILL.md`、`.trae/skills/access-plugin-dev/SKILL.md`、`.trae/skills/plugin-converter/SKILL.md`。
|
||||
- `.codex/skills` 是指向 `.trae/skills` 的目录链接;更新技能只维护 `.trae/skills`,不要复制第二份。
|
||||
|
||||
### 插件市场身份与卡片操作
|
||||
|
||||
- 插件唯一身份是 `fullName`:有作者时为 `author/name`,内置插件为其 `name`。数据库判重、同步/安装匹配、前端列表 `key` 和 Fast Crud `rowKey` 都必须使用 `fullName`,不得退回仅按 `name` 匹配。
|
||||
- 新增或导入本地插件必须生成 `fullName`;旧插件若缺失 `fullName`,应在迁移或兼容更新时按作者和名称补齐,避免同名不同作者的插件互相覆盖或误判已安装。
|
||||
- 插件分为四类:内置插件(无作者,需通过内置插件接口获取)、导入/自开发插件(无 `developerId`)、云端同步未安装插件(有 `developerId` 且 `installed=false`)、云端已安装插件(有 `developerId` 且 `installed=true`)。
|
||||
- 云端插件是否属于当前用户,只能以当前站点绑定账号的 `userId === developerId` 判断;不要以 `localEditable` 或插件名称、作者名称等字段代替。
|
||||
- 插件管理卡片所有类型都必须有“配置”入口。导入/自开发插件应显示编辑、复制、发布、删除;当前用户自己的已安装云端插件应显示编辑、复制、发布,但云端插件一律不显示删除。云端插件按 `installed` 显示安装或卸载,且管理操作和卸载入口可同时存在,不能互相覆盖。
|
||||
|
||||
### 后置任务(AfterTask)机制
|
||||
|
||||
- 概念:流水线主体运行结束后,按触发条件执行的任务;插件声明 `supportAfterTask: true` 后即可在后置任务中复用(如吊销旧证书 `CertRevokeOld`)。触发条件 `when`:`success`(主体执行成功)、`error`(主体失败)、`turnToSuccess`(主体被手动标记成功)。
|
||||
- 实现位置:`packages/core/pipeline/src/core/executor.ts`(`runAfterTasks` / `runOneAfterTask` / `executeAfterTaskPlugin`)与 `plugin/api.ts`。
|
||||
- **后置任务与普通任务步骤的执行链路完全一致**:`executeAfterTaskPlugin` 构造无 strategy 的伪 Step 后调用共用 `executePlugin`;不要给 executePlugin 增加差异化参数——后置任务与普通插件唯一区别是触发时机判断(`matchAfterTaskWhen`)。
|
||||
- 后置任务失败:`runAfterTasks` 聚合错误(格式 `后置任务[标题]执行失败:...`)并 throw → `runWithHistory` 将流水线整体标记为失败 → 统一走 error 通知;不再单独发送后置任务失败通知。
|
||||
- 后置任务没有内置等待时长:需要等待时由插件自身处理(`AbstractTaskPlugin.sleep(ms)` + `checkSignal()`)。
|
||||
- 未触发/禁用:写日志并标记 skip/disabled(与任务同语义),状态存 `afterTask.status`,日志 key 为 `afterTask.{id}`。
|
||||
- 通知在流水线整体(主体 + 后置任务)执行完成后发送,内容附加后置任务执行结果摘要。
|
||||
|
||||
### 流水线执行与状态写入规则
|
||||
|
||||
- `run()` 时序:`notification("start")` → `runtime.start(pipeline)` → `runtime.clearNotificationStatus()`(start 时机状态不参与显示)→ `runWithHistory(主体 + 后置任务)` → 成功/失败通知。`clearNotificationStatus` 不得移到 `notification("start")` 之前。
|
||||
- **插件主动返回 "skip" 时,runStep 仍必须执行结果处理**(把实例输出属性写回 `step.status.output` / files 等):否则保存的 lastRuntime 缺少输出,下次运行按上次结果做判断时(如 SkipWhenSucceed、证书未过期判断 `readLastCert`)会读不到上次证书,导致不跳过、重新申请(曾出 bug:第二次运行状态丢失)。SkipWhenSucceed 策略跳过的步骤,输出/files 直接从 lastStatus 拷贝(executePlugin 返回 `skipped: true`),此时可跳过结果处理。
|
||||
- 事件机制:`emit.ts` 为泛型 `PipelineEmitter<T> implements TaskEmitter<T>`,`emit(name, event)` 直接传事件对象,不再包装 `{pipeline, step, event}`;`PipelineEvent<T> = { cert: T; runnableId: string; pipelineId: number }`(`plugin/api.ts`)。
|
||||
- 证书申请成功事件:`base-convert.ts` `emitCertApplySuccess` 发 `{ cert, pipelineId: ctx.pipeline.id, runnableId: ctx.step.id }`;后端监听在 `auto-pipeline-emitter-register.ts`,写入证书仓库(`updateCertByPipelineId(pipelineId, cert, fromType, taskId)`,userId/projectId 从 pipeline 实体读取)。
|
||||
|
||||
### 流水线与插件地图
|
||||
|
||||
- `packages/core/pipeline/src/index.ts`:核心导出入口,导出 `core`、`dt`、`access`、`registry`、`plugin`、`context`、`decorator`、`service`、`notification`。
|
||||
- `packages/core/pipeline/src/plugin`:任务插件抽象,主要包括:
|
||||
- `AbstractTaskPlugin`
|
||||
- `IsTaskPlugin`
|
||||
- `TaskInput`
|
||||
- `pluginRegistry`
|
||||
- `packages/core/pipeline/src/access`:授权插件抽象,主要包括:
|
||||
- `BaseAccess`
|
||||
- `IsAccess`
|
||||
- `AccessInput`
|
||||
- `accessRegistry`
|
||||
- `packages/core/pipeline/src/dt/pipeline.ts`:`Pipeline`、`Stage`、`Task`、`RunStrategy` 等流水线数据结构。
|
||||
- `packages/core/pipeline/src/core`:执行器、上下文、运行历史、文件存储等。
|
||||
- `packages/core/pipeline/src/service`:CNAME、事件、配置、邮件、URL 等 pipeline service 接口。
|
||||
- `packages/ui/certd-server/src/plugins`:后端内置服务商、DNS、部署、通知等插件。
|
||||
- `packages/ui/certd-server/src/plugins/plugin-cert`:证书申请核心插件。
|
||||
- `packages/ui/certd-server/src/plugins/plugin-lib`:后端插件 helper/access。
|
||||
- `packages/plugins/plugin-lib/src/cert`:`CertReader`、`CertConverter`、DNS Provider 公共能力。
|
||||
- `packages/plugins/plugin-lib/src/cert/dns-provider`:`AbstractDnsProvider`、`dnsProviderRegistry`、`DomainParser`。
|
||||
- ACME 协议、EAB、账号、订单、挑战流程:优先看 `packages/core/acme-client` 或 `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts`。
|
||||
- 流水线执行、任务生命周期、输入输出、注册机制:看 `packages/core/pipeline`。
|
||||
- 单个云厂商 DNS 验证、证书部署、API 调用失败:改对应 `packages/ui/certd-server/src/plugins/plugin-xxx`。
|
||||
- 通用证书读取、DNS Provider 公共能力、格式转换:改 `packages/plugins/plugin-lib`。
|
||||
- 后端业务数据、接口、实体、权限、迁移:改 `packages/ui/certd-server/src/modules` 与 `src/controller`。
|
||||
- 表单、列表、插件配置 UI:改 `packages/ui/certd-client/src/views/certd` 及对应 `src/api`。
|
||||
|
||||
## 其他注意事项
|
||||
|
||||
### 旧版数据兼容
|
||||
|
||||
- 新增插件参数时,必须要考虑旧版数据兼容,比如新增一个deployType参数,有两种值:`default`和`custom`,需要在使用时判空,走旧版逻辑。
|
||||
|
||||
## 前端路由与国际化
|
||||
|
||||
- 路由 `meta.title` 是 **i18n 国际化 key**,必须在 `src/locales/langs/zh-CN/` 和 `src/locales/langs/en-US/` 对应的模块文件中添加翻译。
|
||||
- 示例:路由 `title: "certd.auditLog"` 需要在中英 locales 文件中有对应 key(`"certd.auditLog": "操作日志"` / `"certd.auditLog": "Audit Log"`)。
|
||||
- 菜单通过路由自动生成,需设置 `meta.isMenu: true` 才会出现在左侧菜单。
|
||||
- Plus 版功能菜单需设置 `meta.show: () => { const settingStore = useSettingStore(); return settingStore.isPlus; }`。
|
||||
|
||||
## 审计日志
|
||||
|
||||
- 审计日志是 Plus 版功能,非 Plus 版不会写入。
|
||||
- Controller 继承 `BaseController`,通过 `this.auditLog({ content: "xxx" })` 记录日志。
|
||||
- Controller 中的 `@Post("/add", { summary: "xxxx" })`, 这个summary是必须要的,他是日志action字段的来源
|
||||
- `getAuditType()` 返回类型常量,中间件自动从 ctx.path 判定 scope(`/api/sys/` → system,其他 → user)。
|
||||
- 操作日志有系统级(scope=system)和用户级(scope=user)区分。
|
||||
@@ -0,0 +1,144 @@
|
||||
# 自定义 ACME 功能开发计划
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
Certd 目前仅支持内置 CA(Let's Encrypt / Google / ZeroSSL / SSL.com / litessl / LE 测试环境)。
|
||||
目标:支持用户**自定义 ACME 证书颁发机构**——用户可填写任意符合 RFC 8555 的 ACME Directory URL
|
||||
(内网 CA、企业 CA、其他公共 ACME 服务商),可选 EAB 授权,走完整的申请、续期、吊销流程。
|
||||
|
||||
## 二、需求确认(已与用户确认)
|
||||
|
||||
1. **Directory URL 填写位置**:在「ACME账号授权」中填写(账号与 CA 绑定,续期自动复用账号)。
|
||||
- 代码中 `directoryUrl` 字段、`caType === "custom"` 分支、`AcmeService.getAcmeClientByAccount()` 读取 `account.directoryUrl` 均已预留,架构改动最小。
|
||||
2. **不做系统级公共自定义 CA 账号**:仅用户自建自己的 ACME 账号。
|
||||
3. **自定义 CA 支持 EAB,选填**:填了就用,不填则跳过。
|
||||
4. **吊销链路改造**(用户追加要求):证书写入仓库时同时把 `acmeAccountAccessId` 记录到
|
||||
`cd_cert_info` 表,吊销时直接读表,**不再查询流水线**(旧数据回退兼容)。
|
||||
|
||||
## 三、现状分析
|
||||
|
||||
### 已有基础(无需改动或已预留)
|
||||
|
||||
| 位置 | 现状 |
|
||||
| --- | --- |
|
||||
| `@certd/acme-client` | 底层 `new Client({ directoryUrl })` 直接支持任意 URL,无需改动 |
|
||||
| `AcmeAccountAccess.directoryUrl` 字段 | 已定义,但 `mergeScript` 里 `show: false` 隐藏 |
|
||||
| `AcmeAccountAccess.getDirectoryUrl()` | 已有 `caType === "custom"` 分支:校验必填并返回 `directoryUrl` |
|
||||
| `AcmeAccountInfo` 类型 | 已含 `directoryUrl`、`eab` 字段 |
|
||||
| `AcmeService.getAcmeClientByAccount()` | 已支持 `account.directoryUrl \|\| acme.getDirectoryUrl(...)` |
|
||||
| `AcmeAccountAccess.createAccountInfo()` | EAB 已选填(`eabKid && eabHmacKey` 才构造),custom 不强制 EAB |
|
||||
| 前端 `Dicts.sslProviderDict` | 已含 `custom`("自定义ACME")选项 |
|
||||
|
||||
### 缺口(本次要开发)
|
||||
|
||||
1. `AcmeAccountAccess.caType` 下拉无 `custom` 选项;`directoryUrl` 被隐藏、无必填联动。
|
||||
2. `AcmeAccountAccess` EAB 字段 `show` 条件不含 custom;custom 下应为选填(内置 CA 保持必填)。
|
||||
3. `AcmeService.getAcmeClient()`(无账号临时建账号路径,生成账号时必走)用 `acme.getDirectoryUrl({ sslProvider: "custom" })` 会抛错,需要支持 `directoryUrl` 参数。
|
||||
4. `CertApplyPlugin.sslProvider` 下拉无 `custom` 选项;custom 时 ACME 账号必选、preferredChain/certProfile 显隐、DNS 持久验证限制。
|
||||
5. 吊销链路:`CertInfoService.revoke` 依赖 `resolveRevokeParams` 查流水线解析参数,需改为读表。
|
||||
6. 数据库迁移:`cd_cert_info` 新增 `acme_account_access_id` 列。
|
||||
|
||||
## 四、改动清单
|
||||
|
||||
### 4.1 数据层:cd_cert_info 新增 ACME 账号列
|
||||
|
||||
- **迁移 SQL**:`packages/ui/certd-server/db/migration/v10055__cert_info_acme_account.sql`
|
||||
|
||||
```sql
|
||||
-- 证书仓库记录增加ACME账号授权id:吊销旧证书时直接使用记录上的账号,无需再解析流水线配置
|
||||
ALTER TABLE cd_cert_info ADD COLUMN acme_account_access_id bigint NULL;
|
||||
```
|
||||
|
||||
- **Entity**:`packages/ui/certd-server/src/modules/monitor/entity/cert-info.ts`
|
||||
- 新增字段 `acmeAccountAccessId`(映射 `acme_account_access_id`,nullable)。
|
||||
|
||||
### 4.2 ACME账号授权:支持自定义CA(`access/acme-account-access.ts`)
|
||||
|
||||
1. `caType` 下拉 options 增加 `{ value: "custom", label: "自定义ACME" }`(放首位或末位均可,建议末位并注明"需自备 ACME 服务")。
|
||||
2. `directoryUrl`:
|
||||
- `mergeScript` 改为 `show: ctx.compute(({ form }) => form.access?.caType === "custom")`;
|
||||
- 动态 `required`(custom 时必填);
|
||||
- helper 说明:填写 `https://` 开头的 Directory URL,如内网 CA 端点。
|
||||
3. EAB 字段(`eabKid` / `eabHmacKey`):
|
||||
- `show` 条件增加 `custom`;
|
||||
- custom 下**选填**(mergeScript 动态 `required: false`),内置 CA 保持必填;
|
||||
- `onGenerateAccount()` 中 `needEab` 列表**不加** custom(已满足选填语义)。
|
||||
4. `getDirectoryUrl()` 已有 custom 分支,确认校验信息友好即可。
|
||||
|
||||
### 4.3 AcmeService 支持自定义 Directory URL(`plugin/cert-plugin/acme.ts`)
|
||||
|
||||
1. `AcmeServiceOptions` 增加 `directoryUrl?: string`。
|
||||
2. `getAcmeClient(email)`:
|
||||
- `const directoryUrl = this.options.directoryUrl || acme.getDirectoryUrl({ sslProvider: this.sslProvider, pkType: this.options.privateKeyType })`;
|
||||
- custom 时若未传 `directoryUrl` 给出明确报错("自定义ACME需要填写Directory URL")。
|
||||
3. `revokeCert` 方式二(证书私钥方式)同样优先用 `this.options.directoryUrl`。
|
||||
4. `buildUrlMapping`:自定义域名不在内置映射表,天然走直连/用户反代,无需改动。
|
||||
|
||||
### 4.4 证书申请任务支持自定义CA(`plugin/cert-plugin/apply.ts`)
|
||||
|
||||
1. `sslProvider` 下拉 options 增加 `{ value: "custom", label: "自定义ACME", icon: "..." }`。
|
||||
2. `onInit()` 构造 `AcmeService` 时:
|
||||
- 若 `sslProvider === "custom"`,读取 `this.acmeAccountAccessId` 对应 access 的 `account.directoryUrl`,传入 `AcmeServiceOptions.directoryUrl`;
|
||||
- custom 且未选 ACME 账号时直接报错("自定义ACME必须选择对应的ACME账号")。
|
||||
3. 校验:
|
||||
- `doCertApply()` 中 custom 时强制 `acmeAccount`(version 1 也强制,version 2 已有强制逻辑);
|
||||
- `sslProvider === "custom"` 且 `challengeType === "dns-persist"` 时提示不支持(dns-persist 是 Let's Encrypt 特有机制)。
|
||||
4. 表单显隐(现有 mergeScript 天然兼容,需回归确认):
|
||||
- `preferredChain`:custom 不在 `preferredChainSupportedProviders`,`show: false` ✓;
|
||||
- `certProfile`:仅 letsencrypt 显示 ✓;
|
||||
- `getCommonAcmeAccount()`:custom 时 `${sslProvider}CommonAcmeAccountAccessId` 不存在,返回 null ✓。
|
||||
5. `getCheckChangeInputKeys()` 已含 `sslProvider`,切换 CA 会触发重新申请 ✓。
|
||||
|
||||
### 4.5 吊销链路改造(核心,用户指定方案)
|
||||
|
||||
**写入侧**:`packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts`
|
||||
|
||||
1. `updateCertByPipelineId(pipelineId, cert, fromType, taskId)`:
|
||||
- 已查询 `pipeline` 实体,从 `pipeline.content` 解析证书申请任务输入(复用/提取 `parseCertApplyInput` 为模块内方法),取出 `acmeAccountAccessId`;
|
||||
- 传入 `updateCert()`,写入记录(含占位记录复用更新场景)。
|
||||
2. `UploadCertReq` 增加 `acmeAccountAccessId?: number`;`updateCert()` 落库该字段。
|
||||
|
||||
**吊销侧**:`CertInfoService.revoke(id, userId, projectId)`
|
||||
|
||||
1. 读 `entity.acmeAccountAccessId`:
|
||||
- **有值(新数据)**:`accessService.getAccessById(acmeAccountAccessId, true, userId, projectId)` → `access.getAccount()` 得到 `acmeAccount`,`sslProvider = acmeAccount.caType`,`useProxy/reverseProxy` 不传(`resolveUrlMapping` 有"直连失败自动启用代理"兜底)→ `acmeService.revokeCert({ cert, acmeAccount })`,**不再查流水线**;
|
||||
- **为空(旧数据 / upload 来源)**:回退现有 `resolveRevokeParams` 查流水线逻辑,保证旧版数据可吊销。
|
||||
2. `createAcmeService()` 不再需要从流水线取 `useProxy/reverseProxy`(保留参数兼容即可)。
|
||||
|
||||
**事件链路**:`modules/auto/auto-pipeline-emitter-register.ts` 无需改动
|
||||
(`updateCertByPipelineId` 内部自行解析流水线配置落库)。
|
||||
|
||||
### 4.6 前端
|
||||
|
||||
- `Dicts.sslProviderDict` 已有 `custom`,无需改;
|
||||
- ACME账号授权、证书申请任务的表单 schema 均来自后端插件定义(`@AccessInput` / `@TaskInput`),前端组件零改动;
|
||||
- 回归确认 `icon-select`(sslProvider)与 `a-select`(caType)能正常渲染新增选项。
|
||||
|
||||
## 五、实施顺序(TDD)
|
||||
|
||||
1. **写测试(RED)**:
|
||||
- `acme-account-access.test.ts`:custom + directoryUrl 生成账号(含 EAB 选填)、缺 directoryUrl 报错;
|
||||
- `acme.test.ts`:`AcmeService` 传/不传 `directoryUrl` 时 `getAcmeClient` 使用正确端点;
|
||||
- `apply.test.ts`:custom + ACME账号 申请链路(mock acme client)、custom 未选账号报错;
|
||||
- `cert-info-service.test.ts`:写入时 `acmeAccountAccessId` 落库;吊销读表(mock accessService)不再解析流水线;旧数据(列为空)回退旧逻辑。
|
||||
2. **4.1 数据层**(迁移 SQL + entity)。
|
||||
3. **4.3 AcmeService directoryUrl 支持**。
|
||||
4. **4.2 ACME账号授权 custom 支持**。
|
||||
5. **4.4 证书申请任务 custom 支持**。
|
||||
6. **4.5 吊销链路改造**。
|
||||
7. **4.6 前端回归验证**。
|
||||
8. 全量单测 + lint。
|
||||
|
||||
## 六、测试与验证
|
||||
|
||||
- 后端单测:`cd packages\ui\certd-server && npm run test:unit`(聚焦 `cert-info-service`、`acme-account-access`、`acme`、`apply` 相关测试);
|
||||
- 改动文件 lint:`cd packages\ui\certd-server && npm run lint`;
|
||||
- 手动验证:新建 caType=custom 的 ACME 账号(填 Directory URL,可选 EAB)→ 生成账号 → 证书申请任务选「自定义ACME」+ 该账号 → 申请成功 → 吊销旧证书(观察不再解析流水线)。
|
||||
|
||||
## 七、风险与兼容性
|
||||
|
||||
1. **旧版数据兼容**:`acme_account_access_id` 列可空,旧记录吊销回退查流水线逻辑,行为不变。
|
||||
2. **dns-persist 限制**:自定义 CA 不支持 DNS 持久验证(LE 特有 `_validation-persist` 机制),申请任务做显式校验提示。
|
||||
3. **代理/反代**:吊销时不再携带任务级 `useProxy/reverseProxy`,依赖 `resolveUrlMapping` 直连失败自动启用代理的兜底;自定义 CA 通常内网/直连可达,影响可接受。若后续需要可在表中再增列。
|
||||
4. **无数据库以外的结构变更**;`updateDomains` 占位记录不强制写账号列(申请成功更新时补齐)。
|
||||
5. 内置 CA 行为完全不变(`directoryUrl` 为空时走原逻辑)。
|
||||
@@ -1,48 +1,54 @@
|
||||
# Certd
|
||||
# Certd
|
||||
|
||||
[English](./README_en.md) | [中文](./README.md)
|
||||
中文 | [English](./README_en.md)
|
||||
|
||||
Certd® 是一个免费的全自动证书管理系统,让你的网站证书永不过期。
|
||||
Certd® 是一个免费的全自动证书管理系统,让你的网站证书永不过期。
|
||||
后缀d取自linux守护进程的命名风格,意为证书守护进程
|
||||
|
||||
|
||||
>首创流水线申请部署证书模式,已被多个项目“借鉴”,被抄也是一种成功。
|
||||
> 首创流水线申请部署证书模式,已被多个项目“借鉴”,被抄也是一种成功。
|
||||
|
||||
> 关于证书续期:
|
||||
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
|
||||
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
|
||||
>* 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
||||
|
||||
>
|
||||
> - 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
|
||||
> - 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
|
||||
> - 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
||||
|
||||
> 流水线数量现已调整为无限制,欢迎大家使用
|
||||
|
||||
| 官方开源地址: | |
|
||||
| ------------------------------------------ | ---------------------------------------------------------------- |
|
||||
| [Github](https://github.com/certd/certd) |  |
|
||||
| [Gitee](https://gitee.com/certd/certd) |  |
|
||||
| [AtomGit](https://atomgit.com/certd/certd) |  |
|
||||
|
||||
## 一、特性
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书(支持所有注册商注册的域名,支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式)
|
||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等70+部署插件)
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
|
||||
* 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
|
||||
* 私有化部署,数据保存本地,安装简单快捷,镜像由Github Actions构建,过程公开透明
|
||||
* 授权加密,站点隐藏,2FA,密码防爆破等多重安全保障
|
||||
* 支持SQLite,PostgreSQL、MySQL多种数据库
|
||||
* 开放接口支持
|
||||
* 站点证书监控
|
||||
* 多用户管理
|
||||
* 多语言支持(中英双语切换)
|
||||
* 各版本向下兼容,一键无忧升级
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
- **全自动申请证书**: 支持所有注册商注册的域名,支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
|
||||
- **全自动部署更新证书**: 目前支持部署到主机、阿里云、腾讯云等110+部署插件
|
||||
- **多种证书格式**: 支持pem、pfx、der、jks、p7b
|
||||
- **免费通配符域名/泛域名证书**: 支持多个域名打到一个证书上
|
||||
- **多种通知方式**: 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
|
||||
- **私有化部署**: 数据保存本地,安装简单快捷,镜像由Github Actions构建,过程公开透明
|
||||
- **多重安全保障**: 授权加密,站点隐藏,2FA,密码防爆破等多重安全保障
|
||||
- **多数据库支持**:支持SQLite、PostgreSQL、MySQL、MariaDB
|
||||
- **开放接口支持**: 提供RESTful API接口,方便集成到其他系统
|
||||
- **站点证书监控**: 定时监控网站证书的过期时间
|
||||
- **多用户管理**: 用户可以管理自己的证书流水线
|
||||
- **项目管理**: 企业级项目管理模式
|
||||
- **多语言支持**: 中英双语切换
|
||||
- **无忧升级**: 版本向下兼容
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 二、在线体验
|
||||
|
||||
官方Demo地址,自助注册后体验
|
||||
官方Demo地址,自助注册后体验
|
||||
|
||||
https://certd.handfree.work/
|
||||
|
||||
> 注意数据将不定期清理,不定期停止定时任务,生产使用请自行部署
|
||||
> 注意数据将不定期清理,不定期停止定时任务,生产使用请自行部署
|
||||
> 包含敏感信息,务必自己本地部署进行生产使用
|
||||
|
||||

|
||||
@@ -52,127 +58,189 @@ https://certd.handfree.work/
|
||||
仅需3步,让你的证书永不过期
|
||||
|
||||
### 1. 创建证书流水线
|
||||
|
||||

|
||||
|
||||
> 添加成功后,就可以直接运行流水线申请证书了
|
||||
|
||||
### 2. 添加部署任务
|
||||
|
||||
当然我们一般需要把证书部署到应用上,certd支持海量的部署插件,您可以根据自身实际情况进行选择,比如部署到Nginx、阿里云、腾讯云、K8S、CDN、宝塔、1Panel等等
|
||||
|
||||
此处演示部署证书到主机的nginx上
|
||||
此处演示部署证书到主机的nginx上
|
||||

|
||||
|
||||
如果目前的部署插件都无法满足,您也可以手动下载,然后自行部署
|
||||
如果目前的部署插件都无法满足,您也可以手动下载,然后自行部署
|
||||

|
||||
|
||||
### 3. 定时运行
|
||||
|
||||

|
||||
|
||||
|
||||
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
||||
-------> [点我查看详细使用步骤演示](./step.md) <--------
|
||||
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
||||
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
||||
-------> [点我查看详细使用步骤演示](./step.md) <--------
|
||||
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
||||
|
||||
更多教程请访问官方文档 [certd.docmirror.cn](https://certd.docmirror.cn/guide/)
|
||||
|
||||
|
||||
|
||||
## 四、私有化部署
|
||||
|
||||
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全
|
||||
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全
|
||||
|
||||
您可以根据实际情况从如下方式中选择一种方式进行私有化部署:
|
||||
|
||||
1. 【推荐】[Docker方式部署 ](https://certd.docmirror.cn/guide/install/docker/)
|
||||
2. 【推荐】[宝塔面板方式部署 ](https://certd.docmirror.cn/guide/install/docker/)
|
||||
3. 【推荐】[1Panel面板方式部署](https://certd.docmirror.cn/guide/install/1panel/)
|
||||
4. 【推荐】[雨云一键部署](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2) : 首充翻倍,每月仅需2.2元
|
||||
[<img src="https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-cn.svg">](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2)
|
||||
5. 【不推荐】[源码方式部署 ](https://certd.docmirror.cn/guide/install/source/)
|
||||
4. 【推荐】[雨云一键部署](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2) : 首充翻倍,每月仅需2.2元
|
||||
[<img src="https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-cn.svg">](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2)
|
||||
|
||||
5. 【推荐】[一键安装脚本](https://certd.docmirror.cn/guide/install/docker/)(自动安装 Docker,Certd):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://gitee.com/certd/certd/raw/v2/docker/run/install.sh | bash
|
||||
```
|
||||
|
||||
6. 【不推荐】[源码方式部署 ](https://certd.docmirror.cn/guide/install/source/)
|
||||
|
||||
#### Docker镜像说明:
|
||||
* 国内镜像地址:
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7`、`[version]-armv7`
|
||||
* DockerHub地址:
|
||||
* `https://hub.docker.com/r/greper/certd`
|
||||
* `greper/certd:latest`
|
||||
* `greper/certd:armv7`、`greper/certd:[version]-armv7`
|
||||
* GitHub Packages地址:
|
||||
* `ghcr.io/certd/certd:latest`
|
||||
* `ghcr.io/certd/certd:armv7`、`ghcr.io/certd/certd:[version]-armv7`
|
||||
|
||||
* 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||
* [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml)
|
||||
##### 1. 镜像地址格式:
|
||||
|
||||
```
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:[version-][system-][latest/stable]
|
||||
------------ ↑ 镜像地址 ------------- ↑ 镜像名 -- ↑指定版本- ↑基础系统- ↑最新版本类型 (中括号表示可选)
|
||||
|
||||
```
|
||||
##### 2. 版本标签:
|
||||
|
||||
**最新版本标签:**
|
||||
|
||||
| 版本 | 标签 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 最新预览版【默认】 | `certd:latest` | 指向最新开发版本,包含最新功能,但稳定性不如稳定版 |
|
||||
| 最新稳定版 | `certd:stable` | 指向经过充分测试的生产就绪版本,推荐生产环境使用 |
|
||||
|
||||
**系统分支版本:**
|
||||
|
||||
> 根据基础镜像不同,分为如下三个分支版本,没有特殊需求选择默认的即可(他们功能是一样的)
|
||||
|
||||
| 系统版本 | 版本标签 | 基础系统 | 说明 | 稳定版标签 | 指定版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| alpine【默认】 | `certd:latest` | Alpine Linux | 默认版本,镜像体积小 | `certd:stable` | `certd:1.43.0` |
|
||||
| slim | `certd:slim` | Debian slim | 基于glibc,dns解析兼容性好 | `certd:slim-stable` | `certd:1.43.0-slim` |
|
||||
| armv7 | `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:armv7-stable` | `certd:1.43.0-armv7` |
|
||||
|
||||
##### 3. 镜像地址:
|
||||
|
||||
| 镜像仓库 | 最新预览版 | slim | armv7 |
|
||||
| --- | --- | --- | --- |
|
||||
| 阿里云【默认】 | `registry.cn-shenzhen.aliyuncs.com/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
| Docker Hub | `certd/certd:latest` | `certd:slim` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
|
||||
> 1. 后面的各个版本省略了镜像地址,使用时需要将镜像地址拼接完整。
|
||||
> 2. 如果您不确定使用哪个版本,请使用默认的 `certd:latest` 版本。
|
||||
|
||||
最新版本号:
|
||||

|
||||
|
||||
|
||||
##### 3. 镜像构建说明:
|
||||
|
||||
- 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||
- [点我查看预览版构建日志](https://github.com/certd/certd/actions/workflows/release-image.yml)
|
||||
- [点我查看稳定版发布日志](https://github.com/certd/certd/actions/workflows/stable-release.yml)
|
||||
|
||||

|
||||
|
||||
> 注意:
|
||||
> * 本应用存储的证书、授权信息等属于高度敏感数据,请做好安全防护
|
||||
> * 请务必使用HTTPS协议访问本应用,避免被中间人攻击
|
||||
> * 请务必使用web应用防火墙防护本应用,防止XSS、SQL注入等攻击
|
||||
> * 请务必做好服务器本身的安全防护,防止数据库泄露
|
||||
> * 请务必做好数据备份,避免数据丢失
|
||||
> * [更多安全生产建议点我](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
##### 4. 安全注意事项:
|
||||
|
||||
> - 本应用存储的证书、授权信息等属于高度敏感数据,请做好安全防护
|
||||
> - 请务必使用HTTPS协议访问本应用,避免被中间人攻击
|
||||
> - 请务必使用web应用防火墙防护本应用,防止XSS、SQL注入等攻击
|
||||
> - 请务必做好服务器本身的安全防护,防止数据库泄露
|
||||
> - 请务必做好数据备份,避免数据丢失
|
||||
> - [更多安全生产建议点我](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
|
||||
## 五、生态
|
||||
|
||||
### 1. 客户端工具 SSL-Assistant
|
||||
`SSL Assistant` 是一个运行于主机上的证书部署管理助手客户端。
|
||||
支持自动扫描主机`Nginx`配置,然后从`Certd`拉取证书并部署。
|
||||
|
||||
### 1. 官方客户端 Certd Client
|
||||
|
||||
`Certd Client` 是 Certd 官方证书部署客户端,运行于应用服务器上。自动发现本机 Nginx、Apache 和 IIS 站点,从 Certd 获取新证书并部署到本机。适用于不希望服务器开放 SSH、无法由 Certd 直接访问目标服务器的场景。
|
||||
|
||||
项目主页:[AtomGit](https://atomgit.com/certd/certd-client/) | [GitHub](https://github.com/certd/certd-client/)
|
||||
下载地址:[AtomGit Releases](https://atomgit.com/certd/certd-client/releases) | [GitHub Releases](https://github.com/certd/certd-client/releases)
|
||||
|
||||
### 2. 第三方客户端 SSL-Assistant
|
||||
|
||||
`SSL Assistant` 是一个运行于主机上的证书部署管理助手客户端。
|
||||
支持自动扫描主机`Nginx`配置,然后从`Certd`拉取证书并部署。
|
||||
在不想暴露ssh主机密码情况下,该工具非常好用。
|
||||
|
||||
开源地址: https://github.com/Youngxj/SSL-Assistant
|
||||
|
||||
|
||||
## 六、更多帮助
|
||||
|
||||
请访问官方文档:[https://certd.docmirror.cn/](https://certd.docmirror.cn/guide/)
|
||||
|
||||
* 升级方法:[升级方法](https://certd.docmirror.cn/guide/install/upgrade/)
|
||||
* 常见问题:[忘记密码](https://certd.docmirror.cn/guide/use/forgotpasswd/)
|
||||
* 多数据库:[多数据库配置](https://certd.docmirror.cn/guide/install/database/)
|
||||
* 站点安全:[站点安全特性](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
* 更新日志:[CHANGELOG](./CHANGELOG.md)
|
||||
|
||||
- 升级方法:[升级方法](https://certd.docmirror.cn/guide/install/upgrade/)
|
||||
- 常见问题:[忘记密码](https://certd.docmirror.cn/guide/use/forgotpasswd/)
|
||||
- 多数据库:[多数据库配置](https://certd.docmirror.cn/guide/install/database/)
|
||||
- 站点安全:[站点安全特性](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
- 更新日志:[CHANGELOG](./CHANGELOG.md)
|
||||
|
||||
## 七、联系作者
|
||||
|
||||
如有疑问,欢迎加入群聊(请备注certd)
|
||||
|
||||
| 加群 | 微信群 | QQ群 |
|
||||
|---------|-------|-------|
|
||||
| 加群 | 微信群 | QQ群 |
|
||||
| ------ | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| 二维码 | <img height="230" src="./docs/guide/contact/images/wx.png"> | <img height="230" src="./docs/guide/contact/images/qq.png"> |
|
||||
|
||||
也可以加作者好友
|
||||
|
||||
| 加作者好友 | 微信 QQ |
|
||||
|---------|-------------------------------------------------------------|
|
||||
| 二维码 | <img height="230" src="./docs/guide/contact/images/me.png"> |
|
||||
| 加作者好友 | 微信 QQ |
|
||||
| ---------- | ----------------------------------------------------------- |
|
||||
| 二维码 | <img height="230" src="./docs/guide/contact/images/me.png"> |
|
||||
|
||||
## 八、赞助捐赠
|
||||
|
||||
开源为什么要做专业版收费?
|
||||
|
||||
1. 纯靠为爱发电不可持续(比如:我的[dev-sidecar项目](https://github.com/docmirror/dev-sidecar)即便是拥有20K+star,也差点凉凉,幸亏有另外大佬接手用爱发电)
|
||||
2. 没有赞助的项目,作者会比较任性,不会用心倾听用户的心声,不顾用户体验(比如:下意识拒绝需求、频繁破坏性变更升级、全盘推倒重来之类的)
|
||||
3. 没有赞助的项目,交流群的戾气有时候比较重,容易起冲突
|
||||
|
||||
赞助权益:
|
||||
|
||||
1. 可加入专属VIP群,可以获得作者一对一技术支持,必要时可以远程协助
|
||||
2. 您的需求我们将优先实现,并且可能将作为专业版功能提供
|
||||
3. 获得专业版功能
|
||||
|
||||
|
||||
## 八、捐赠
|
||||
************************
|
||||
支持开源,为爱发电,我已入驻爱发电
|
||||
https://afdian.com/a/greper
|
||||
|
||||
发电权益:
|
||||
1. 可加入发电专属群,可以获得作者一对一技术支持
|
||||
2. 您的需求我们将优先实现,并且将作为专业版功能提供
|
||||
3. 一年期专业版激活码
|
||||
|
||||
专业版特权对比
|
||||
|
||||
| 功能 | 免费版 | 专业版 |
|
||||
|---------|---------------------------------------|--------------------------------|
|
||||
| 免费证书申请 | 免费无限制 | 免费无限制 |
|
||||
| 域名数量 | 无限制 | 无限制 |
|
||||
| 证书流水线条数 | 无限制 | 无限制 |
|
||||
| 站点证书监控 | 限制1条 | 无限制 |
|
||||
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖 |
|
||||
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
|
||||
> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product)
|
||||
> https://app.handfree.work/subject/#/app/certd/product
|
||||
> app.handfree.work是Certd官方激活码购买平台
|
||||
|
||||
|
||||
************************
|
||||
专业版、商业版特权对比
|
||||
|
||||
| 功能 | 免费版 | 专业版 | 商业版 |
|
||||
| ---------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------ | --------------------------------------------------- |
|
||||
| 证书申请 | 无限制 | 无限制 | 无限制 |
|
||||
| 证书域名数量 | 无限制 | 无限制 | 无限制 |
|
||||
| 证书流水线条数 | 无限制 | 无限制 | 无限制 |
|
||||
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 | 同专业版 |
|
||||
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 | 同专业版 |
|
||||
| 站点监控 | 限制1条 | 无限制 | 无限制 |
|
||||
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 | 同专业版 |
|
||||
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 | 商业版技术支持 |
|
||||
| 站点个性化 | 无 | 无 | 可自定义站点名称、Logo等,移除Certd元素,首页警告等 |
|
||||
| 套餐功能 | 无 | 无 | 支持配置套餐供用户购买 |
|
||||
| 数据统计 | 无 | 无 | 支持站点各类统计数据 |
|
||||
| 插件管理 | 无 | 无 | 支持公共EAB设置,插件选项配置 |
|
||||
| 是否可商用 | 不允许 | 不允许 | 可对外运营 |
|
||||
|
||||
## 九、贡献代码
|
||||
|
||||
@@ -188,15 +256,16 @@ https://afdian.com/a/greper
|
||||
</a>
|
||||
|
||||
## 十、 开源许可
|
||||
* 本项目遵循 GNU Affero General Public License(AGPL)开源协议。
|
||||
* 允许个人和公司内部自由使用、复制、修改和分发本项目,未获得商业授权情况下禁止任何形式的商业用途
|
||||
* 未获得商业授权情况下,禁止任何对logo、版权信息及授权许可相关代码的修改。
|
||||
* 如需商业授权,请联系作者。
|
||||
|
||||
- 本项目遵循 GNU Affero General Public License(AGPL)开源协议。
|
||||
- 允许个人和公司内部自由使用、复制、修改和分发本项目,未获得商业授权情况下禁止任何形式的商业用途
|
||||
- 未获得商业授权情况下,禁止任何对logo、版权信息及授权许可相关代码的修改。
|
||||
- 如需商业授权,请联系作者。
|
||||
|
||||
## 十一、我的其他项目(求Star)
|
||||
|
||||
| 项目名称 | stars | 项目描述 |
|
||||
| --------- |--------- |----------- |
|
||||
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | 基于vue3的crud快速开发框架 |
|
||||
| [dev-sidecar](https://github.com/docmirror/dev-sidecar/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"/> | 直连访问github工具,无需FQ,解决github无法访问的问题 |
|
||||
| 项目名称 | stars | 项目描述 |
|
||||
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | 基于vue3的crud快速开发框架 |
|
||||
| [dev-sidecar](https://github.com/docmirror/dev-sidecar/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"/> | 直连访问github工具,无需FQ,解决github无法访问的问题 |
|
||||
| [winsvc-manager](https://github.com/greper/winsvc-manager/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/greper/winsvc-manager?logo=github"/> | 可视化包装应用成为一个Windows服务,使其后台运行 |
|
||||
|
||||
@@ -1,37 +1,46 @@
|
||||
# Certd
|
||||
# Certd
|
||||
|
||||
[English](./README_en.md) | [中文](./README.md)
|
||||
[中文](./README.md) | English
|
||||
|
||||
Certd® is a free, fully automated certificate management system that ensures your website certificates never expire. The suffix 'd' is inspired by the naming convention of Linux daemons, representing a certificate daemon.
|
||||
|
||||
> We pioneered the pipeline-based certificate application and deployment model, which has been "referenced" by multiple projects. Being copied is also a form of success.
|
||||
|
||||
> Regarding certificate renewal:
|
||||
>* In fact, it's impossible to renew or reissue a certificate without modifying the certificate file itself.
|
||||
>* What we refer to as renewal is essentially applying for a new certificate following the full process and redeploying it.
|
||||
>* Free certificates expire in 90 days, which may be shortened in the future. Therefore, automated deployment is essential.
|
||||
>
|
||||
> - In fact, it's impossible to renew or reissue a certificate without modifying the certificate file itself.
|
||||
> - What we refer to as renewal is essentially applying for a new certificate following the full process and redeploying it.
|
||||
> - Free certificates expire in 90 days, which may be shortened in the future. Therefore, automated deployment is essential.
|
||||
|
||||
> The number of pipelines is now unlimited. Welcome to use it.
|
||||
|
||||
Official Open Source Address:
|
||||
|
||||
[Github](https://github.com/certd/certd) 
|
||||
[Gitee](https://gitee.com/certd/certd) 
|
||||
[AtomGit](https://atomgit.com/certd/certd) 
|
||||
|
||||
## 1. Features
|
||||
|
||||
This project not only supports automated certificate application but also automated certificate deployment and updates, ensuring your certificates never expire.
|
||||
|
||||
* Fully automated certificate application (supports domains registered with all registrars and multiple domain verification methods such as DNS-01, HTTP-01, and CNAME proxy).
|
||||
* Fully automated certificate deployment and updates (currently supports deployment to over 70 plugins, including hosts, Alibaba Cloud, Tencent Cloud, etc.).
|
||||
* Supports wildcard domains/pan-domains, allows multiple domains in a single certificate, and supports various certificate formats such as pem, pfx, der, and jks.
|
||||
* Multiple notification methods, including email, webhook, WeChat Work, DingTalk, Lark, and anpush.
|
||||
* On-premises deployment, local data storage, simple and quick installation. Images are built by Github Actions, with a transparent process.
|
||||
* Multiple security measures, including authorization encryption, site hiding, 2FA, and password brute-force protection.
|
||||
* Supports multiple databases such as SQLite, PostgreSQL, and MySQL.
|
||||
* Open API support.
|
||||
* Site certificate monitoring.
|
||||
* Multi-user management.
|
||||
* Multi-language support (Chinese and English switching).
|
||||
* Downward compatibility across all versions, with one-click worry-free upgrades.
|
||||
- Fully automated certificate application (supports domains registered with all registrars and multiple domain verification methods such as DNS-01, HTTP-01, and CNAME proxy).
|
||||
- Fully automated certificate deployment and updates (currently supports deployment to over 70 plugins, including hosts, Alibaba Cloud, Tencent Cloud, etc.).
|
||||
- Supports wildcard domains/pan-domains, allows multiple domains in a single certificate, and supports various certificate formats such as pem, pfx, der, and jks.
|
||||
- Multiple notification methods, including email, webhook, WeChat Work, DingTalk, Lark, and anpush.
|
||||
- On-premises deployment, local data storage, simple and quick installation. Images are built by Github Actions, with a transparent process.
|
||||
- Multiple security measures, including authorization encryption, site hiding, 2FA, and password brute-force protection.
|
||||
- Supports multiple databases such as SQLite, PostgreSQL, MySQL, and MariaDB.
|
||||
- Open API support.
|
||||
- Site certificate monitoring.
|
||||
- Multi-user management.
|
||||
- Multi-language support (Chinese and English switching).
|
||||
- Downward compatibility across all versions, with one-click worry-free upgrades.
|
||||
|
||||

|
||||
|
||||
## 2. Online Experience
|
||||
|
||||
Visit the official demo site and register to experience it.
|
||||
|
||||
https://certd.handfree.work/
|
||||
@@ -42,14 +51,17 @@ https://certd.handfree.work/
|
||||

|
||||
|
||||
## 3. Usage Tutorial
|
||||
|
||||
Just 3 steps to ensure your certificates never expire.
|
||||
|
||||
### 1. Create a Certificate Pipeline
|
||||
|
||||

|
||||
|
||||
> After successful addition, you can directly run the pipeline to apply for a certificate.
|
||||
|
||||
### 2. Add a Deployment Task
|
||||
|
||||
Normally, we need to deploy certificates to applications. Certd supports a wide range of deployment plugins. You can choose based on your needs, such as deploying to Nginx, Alibaba Cloud, Tencent Cloud, K8S, CDN, Baota, 1Panel, etc.
|
||||
|
||||
Here's a demonstration of deploying certificates to a host's Nginx:
|
||||
@@ -59,6 +71,7 @@ If the current deployment plugins don't meet your needs, you can also download t
|
||||

|
||||
|
||||
### 3. Run Scheduled Tasks
|
||||
|
||||

|
||||
|
||||
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
||||
@@ -68,6 +81,7 @@ If the current deployment plugins don't meet your needs, you can also download t
|
||||
For more tutorials, please visit the official documentation [certd.docmirror.cn](https://certd.docmirror.cn/guide/).
|
||||
|
||||
## 4. On-Premises Deployment
|
||||
|
||||
Since certificates, authorization information, and other data are highly sensitive, please make sure to deploy them on-premises to ensure data security.
|
||||
|
||||
You can choose one of the following deployment methods based on your needs:
|
||||
@@ -76,85 +90,128 @@ You can choose one of the following deployment methods based on your needs:
|
||||
2. 【Recommended】[BT Panel Deployment](https://certd.docmirror.cn/guide/install/docker/)
|
||||
3. 【Recommended】[1Panel Deployment](https://certd.docmirror.cn/guide/install/1panel/)
|
||||
4. 【Recommended】[Rainyun One-Click Deployment](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2_): Double your first recharge, only $2.2 per month.
|
||||
[<img src="https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-cn.svg">](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2_)
|
||||
[<img src="https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-cn.svg">](https://app.rainyun.com/apps/rca/store/6646/?ref=NzExMDQ2_)
|
||||
5. 【Not Recommended】[Source Code Deployment](https://certd.docmirror.cn/guide/install/source/)
|
||||
|
||||
#### Docker Image Information:
|
||||
* Domestic Image Addresses:
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7`, `[version]-armv7`
|
||||
* DockerHub Addresses:
|
||||
* `https://hub.docker.com/r/greper/certd`
|
||||
* `greper/certd:latest`
|
||||
* `greper/certd:armv7`, `greper/certd:[version]-armv7`
|
||||
* GitHub Packages Addresses:
|
||||
* `ghcr.io/certd/certd:latest`
|
||||
* `ghcr.io/certd/certd:armv7`, `ghcr.io/certd/certd:[version]-armv7`
|
||||
|
||||
* Images are built automatically by `Actions`, with a transparent process. Please use them with confidence.
|
||||
* [Click here to view image build logs](https://github.com/certd/certd/actions/workflows/build-image.yml)
|
||||
**Release channels:**
|
||||
|
||||
| Channel | Description |
|
||||
| --- | --- |
|
||||
| `stable` / `slim-stable` | **Stable version**, production-ready and fully tested, recommended for production environments |
|
||||
| `latest` / `slim` / `armv7` | **Preview version**, latest development build with newest features but potentially less stable |
|
||||
|
||||
**Image tags:**
|
||||
|
||||
| Channel | Tag | Versioned Tag | Base System | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **Stable** | `stable` | `[version]-stable` | Alpine Linux | Recommended for production |
|
||||
| | `slim-stable` | `[version]-slim-stable` | Debian slim | Better DNS resolution compatibility |
|
||||
| **Preview** | `latest` | `[version]` | Alpine Linux | Default, small image size |
|
||||
| | `slim` | `[version]-slim` | Debian slim | Better DNS resolution compatibility |
|
||||
| | `armv7` | `[version]-armv7` | Alpine Linux | ARMv7 architecture |
|
||||
|
||||
**Stable version image addresses:**
|
||||
|
||||
| Registry | `stable` | `slim-stable` |
|
||||
| --- | --- | --- |
|
||||
| Aliyun | `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:stable` | `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim-stable` |
|
||||
| Docker Hub | `greper/certd:stable` | `greper/certd:slim-stable` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:stable` | `ghcr.io/certd/certd:slim-stable` |
|
||||
|
||||
**Preview version image addresses:**
|
||||
|
||||
| Registry | `latest` | `slim` | `armv7` |
|
||||
| --- | --- | --- | --- |
|
||||
| Aliyun | `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest` | `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim` | `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7` |
|
||||
| Docker Hub | `greper/certd:latest` | `greper/certd:slim` | `greper/certd:armv7` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `ghcr.io/certd/certd:slim` | `ghcr.io/certd/certd:armv7` |
|
||||
|
||||
> For versioned tags, replace tag name with `[version]-tag`, e.g. replace `stable` with `[version]-stable`
|
||||
|
||||
- Images are built automatically by `Actions`, with a transparent process. Please use them with confidence.
|
||||
- [Click here to view preview version build logs](https://github.com/certd/certd/actions/workflows/release-image.yml)
|
||||
- [Click here to view stable version release logs](https://github.com/certd/certd/actions/workflows/stable-release.yml)
|
||||

|
||||
|
||||
> Note:
|
||||
> * The certificates, authorization information, and other data stored in this application are highly sensitive. Please take appropriate security measures.
|
||||
> * Make sure to use the HTTPS protocol to access this application to avoid man-in-the-middle attacks.
|
||||
> * Make sure to use a web application firewall to protect this application from attacks such as XSS and SQL injection.
|
||||
> * Make sure to secure the server itself to prevent database leakage.
|
||||
> * Make sure to back up your data to avoid data loss.
|
||||
> * [Click here for more production safety suggestions](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
>
|
||||
> - The certificates, authorization information, and other data stored in this application are highly sensitive. Please take appropriate security measures.
|
||||
> - Make sure to use the HTTPS protocol to access this application to avoid man-in-the-middle attacks.
|
||||
> - Make sure to use a web application firewall to protect this application from attacks such as XSS and SQL injection.
|
||||
> - Make sure to secure the server itself to prevent database leakage.
|
||||
> - Make sure to back up your data to avoid data loss.
|
||||
> - [Click here for more production safety suggestions](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
|
||||
## 5. Ecosystem
|
||||
|
||||
### 1. Client Tool: SSL-Assistant
|
||||
### 1. Official Client: Certd Client
|
||||
|
||||
`Certd Client` is the official Certd certificate deployment client that runs on application servers. It automatically discovers local Nginx, Apache, and IIS sites, retrieves new certificates from Certd, and deploys them locally. It is designed for servers that should not expose SSH or cannot be accessed directly by Certd.
|
||||
|
||||
Project Home: [AtomGit](https://atomgit.com/certd/certd-client/) | [GitHub](https://github.com/certd/certd-client/)
|
||||
Downloads: [AtomGit Releases](https://atomgit.com/certd/certd-client/releases) | [GitHub Releases](https://github.com/certd/certd-client/releases)
|
||||
|
||||
### 2. Third-Party Client: SSL-Assistant
|
||||
|
||||
`SSL Assistant` is a certificate deployment and management assistant client that runs on hosts. It supports automatic scanning of the host's `Nginx` configuration and pulling certificates from `Certd` for deployment. This tool is very useful when you don't want to expose your SSH host password.
|
||||
|
||||
Open-source Address: https://github.com/Youngxj/SSL-Assistant
|
||||
|
||||
## 6. More Help
|
||||
|
||||
Please visit the official documentation: [https://certd.docmirror.cn/](https://certd.docmirror.cn/guide/).
|
||||
|
||||
* Upgrade Method: [Upgrade Guide](https://certd.docmirror.cn/guide/install/upgrade/)
|
||||
* Common Issues: [Forgot Password](https://certd.docmirror.cn/guide/use/forgotpasswd/)
|
||||
* Multi-Database: [Multi-Database Configuration](https://certd.docmirror.cn/guide/install/database/)
|
||||
* Site Security: [Site Security Features](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
* Changelog: [CHANGELOG](./CHANGELOG.md)
|
||||
- Upgrade Method: [Upgrade Guide](https://certd.docmirror.cn/guide/install/upgrade/)
|
||||
- Common Issues: [Forgot Password](https://certd.docmirror.cn/guide/use/forgotpasswd/)
|
||||
- Multi-Database: [Multi-Database Configuration](https://certd.docmirror.cn/guide/install/database/)
|
||||
- Site Security: [Site Security Features](https://certd.docmirror.cn/guide/feature/safe/)
|
||||
- Changelog: [CHANGELOG](./CHANGELOG.md)
|
||||
|
||||
## 7. Contact the Author
|
||||
|
||||
If you have any questions, feel free to join the group chat (please mention 'certd' in your message).
|
||||
|
||||
| Join Group | WeChat Group | QQ Group |
|
||||
|---------|-------|-------|
|
||||
| QR Code | <img height="230" src="./docs/guide/contact/images/wx.png"> | <img height="230" src="./docs/guide/contact/images/qq.png"> |
|
||||
| Join Group | WeChat Group | QQ Group |
|
||||
| ---------- | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| QR Code | <img height="230" src="./docs/guide/contact/images/wx.png"> | <img height="230" src="./docs/guide/contact/images/qq.png"> |
|
||||
|
||||
You can also add the author as a friend.
|
||||
|
||||
| Add Author as Friend | WeChat QQ |
|
||||
|---------|-------|-------|
|
||||
| QR Code | <img height="230" src="./docs/guide/contact/images/me.png"> |
|
||||
| Add Author as Friend | WeChat QQ |
|
||||
| -------------------- | ----------------------------------------------------------- |
|
||||
| QR Code | <img height="230" src="./docs/guide/contact/images/me.png"> |
|
||||
|
||||
## 8. Donation
|
||||
************************
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/sponsors/greper)
|
||||
|
||||
---
|
||||
|
||||
Support open-source projects and contribute with love. I've joined Afdian.
|
||||
https://afdian.com/a/greper
|
||||
|
||||
Benefits of Contribution:
|
||||
|
||||
1. Join the exclusive contributor group and get one-on-one technical support from the author.
|
||||
2. Your requests will be prioritized and implemented as professional edition features.
|
||||
3. Receive a one-year professional edition activation code.
|
||||
|
||||
Comparison of Professional Edition Privileges:
|
||||
|
||||
| Feature | Free Edition | Professional Edition |
|
||||
|---------|---------------------------------------|--------------------------------|
|
||||
| Free Certificate Application | Unlimited for free | Unlimited for free |
|
||||
| Number of Domains | Unlimited | Unlimited |
|
||||
| Number of Certificate Pipelines | Unlimited | Unlimited |
|
||||
| Site Certificate Monitoring | Limited to 1 | Unlimited |
|
||||
| Automatic Deployment Plugins | Most plugins such as Alibaba Cloud CDN, Tencent Cloud, QiNiu CDN, Host Deployment, Baota, 1Panel | Synology |
|
||||
| Notifications | Email, Custom Webhook | Email without configuration, WeChat Work, DingTalk, Lark, anpush, ServerChan, etc. |
|
||||
| Feature | Free Edition | Professional Edition |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
|
||||
| Free Certificate Application | Unlimited for free | Unlimited for free |
|
||||
| Number of Domains | Unlimited | Unlimited |
|
||||
| Number of Certificate Pipelines | Unlimited | Unlimited |
|
||||
| Site Certificate Monitoring | Limited to 1 | Unlimited |
|
||||
| Automatic Deployment Plugins | Most plugins such as Alibaba Cloud CDN, Tencent Cloud, QiNiu CDN, Host Deployment, Baota, 1Panel | Synology |
|
||||
| Notifications | Email, Custom Webhook | Email without configuration, WeChat Work, DingTalk, Lark, anpush, ServerChan, etc. |
|
||||
|
||||
************************
|
||||
---
|
||||
|
||||
## 9. Contribute Code
|
||||
|
||||
@@ -170,14 +227,15 @@ Thank you to the following contributors.
|
||||
</a>
|
||||
|
||||
## 10. Open-Source License
|
||||
* This project follows the GNU Affero General Public License (AGPL).
|
||||
* Individuals and companies are allowed to use, copy, modify, and distribute this project freely for internal use. Any form of commercial use is prohibited without obtaining commercial authorization.
|
||||
* Without commercial authorization, any modification of the logo, copyright information, and license-related code is prohibited.
|
||||
* For commercial authorization, please contact the author.
|
||||
|
||||
- This project follows the GNU Affero General Public License (AGPL).
|
||||
- Individuals and companies are allowed to use, copy, modify, and distribute this project freely for internal use. Any form of commercial use is prohibited without obtaining commercial authorization.
|
||||
- Without commercial authorization, any modification of the logo, copyright information, and license-related code is prohibited.
|
||||
- For commercial authorization, please contact the author.
|
||||
|
||||
## 11. My Other Projects (Please Star)
|
||||
|
||||
| Project Name | Stars | Project Description |
|
||||
|----------------|---------------|--------------|
|
||||
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | A fast CRUD development framework based on Vue3. |
|
||||
| Project Name | Stars | Project Description |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| [fast-crud](https://gitee.com/fast-crud/fast-crud/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/fast-crud/fast-crud?logo=github"/> | A fast CRUD development framework based on Vue3. |
|
||||
| [dev-sidecar](https://github.com/docmirror/dev-sidecar/) | <img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"/> | A tool to access GitHub directly without a VPN, solving the problem of inaccessible GitHub. |
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
2
|
||||
@@ -1 +0,0 @@
|
||||
23:23
|
||||
@@ -0,0 +1,95 @@
|
||||
version: '3.3' # 兼容旧版docker-compose
|
||||
services:
|
||||
certd:
|
||||
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号,例如:certd:1.29.0
|
||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
# image: ghcr.io/certd/certd:latest # --------- 如果 报镜像not found,可以尝试其他镜像源
|
||||
# image: greper/certd:latest
|
||||
container_name: certd # 容器名
|
||||
restart: unless-stopped # 自动重启
|
||||
volumes:
|
||||
# ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】
|
||||
- /data/certd:/app/data # 只要修改冒号前面的,冒号后面的/app/data切记切记不要动
|
||||
#- /volume1/docker/certd:/app/data:delegated #群晖使用这个配置
|
||||
# ↓↓↓↓↓ -------------------------------------------------------- 如果走时不准,考虑挂载localtime文件
|
||||
#- /etc/localtime:/etc/localtime
|
||||
#- /etc/timezone:/etc/timezone
|
||||
ports: # 端口映射
|
||||
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号,第二个7001不要动
|
||||
- "7001:7001"
|
||||
# ↓↓↓↓ ---------------------------------------------------------- https端口,可以根据实际情况,是否暴露该端口
|
||||
- "7002:7002"
|
||||
#↓↓↓↓ -------------------------------------------------------------- 如果出现getaddrinfo EAI_AGAIN 或 getaddrinfo ENOTFOUND 错误,可以尝试设置dns
|
||||
# dns:
|
||||
# - 223.5.5.5 # 阿里云公共dns
|
||||
# - 223.6.6.6
|
||||
# # ↓↓↓↓ --------------------------------------------------------- 如果你服务器在腾讯云,可以用这个替换上面阿里云的公共dns
|
||||
# - 119.29.29.29 # 腾讯云公共dns
|
||||
# - 182.254.116.116
|
||||
# # ↓↓↓↓ --------------------------------------------------------- 如果你服务器部署在国外,可以用这个替换上面阿里云的公共dns
|
||||
# - 8.8.8.8 # 谷歌公共dns
|
||||
# - 8.8.4.4
|
||||
# extra_hosts:
|
||||
# # ↓↓↓↓ -------------------------------------------------------- 这里可以配置自定义hosts,外网域名可以指向本地局域网ip地址
|
||||
# - "localdomain.com:192.168.1.3"
|
||||
# # ↓↓↓↓ ------------------------------------------------ 直接使用主机的网络,如果网络问题实在找不到原因,可以尝试打开此参数
|
||||
# network_mode: host
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: "true"
|
||||
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把下面networks的注释放开
|
||||
# networks:
|
||||
# - ip6net
|
||||
environment:
|
||||
# ↓↓↓↓ ----------------------------------------------------- 使用上海东八时区
|
||||
- TZ=Asia/Shanghai
|
||||
# 设置环境变量即可自定义certd配置
|
||||
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
|
||||
# 配置规则: certd_ + 配置项, 点号用_代替
|
||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,docker compose up -d 重建容器之后,管理员密码将改成123456,然后请及时修改回false
|
||||
- certd_system_resetAdminPasswd=false
|
||||
# ↓↓↓ 要使用ipv6,将此配置修改为::
|
||||
- certd_koa_hostname=0.0.0.0
|
||||
|
||||
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
|
||||
# 注意: 选定使用一种数据库之后,不支持更换数据库。
|
||||
# 数据库迁移方法:1、使用新数据库重新部署一套,然后将旧数据同步过去,注意flyway_history表的数据不要同步
|
||||
# #↓↓↓↓ ----------------------------- 使用postgresql数据库,需要提前创建数据库
|
||||
# - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录
|
||||
# - certd_typeorm_dataSource_default_type=postgres # 数据库类型
|
||||
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
|
||||
# - certd_typeorm_dataSource_default_port=5433 # 数据库端口
|
||||
# - certd_typeorm_dataSource_default_username=postgres # 用户名
|
||||
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
|
||||
# - certd_typeorm_dataSource_default_database=certd # 数据库名
|
||||
|
||||
# #↓↓↓↓ ----------------------------- 使用mysql8数据库,需要提前创建数据库 charset=utf8mb4, collation=utf8mb4_bin
|
||||
# - certd_flyway_scriptDir=./db/migration-mysql # 升级脚本目录
|
||||
# - certd_typeorm_dataSource_default_type=mysql # 数据库类型, 或者 mariadb
|
||||
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
|
||||
# - certd_typeorm_dataSource_default_port=3306 # 数据库端口
|
||||
# - certd_typeorm_dataSource_default_username=root # 用户名
|
||||
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
|
||||
# - certd_typeorm_dataSource_default_database=certd # 数据库名
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
|
||||
certd-updater: # 添加 Watchtower 服务
|
||||
image: containrrr/watchtower:latest
|
||||
container_name: certd-updater
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# 配置 自动更新
|
||||
environment:
|
||||
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
|
||||
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
|
||||
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
|
||||
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
|
||||
|
||||
|
||||
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把上面networks的注释放开
|
||||
#networks:
|
||||
# ip6net:
|
||||
# enable_ipv6: true
|
||||
# ipam:
|
||||
# config:
|
||||
# - subnet: 2001:db8::/64
|
||||
@@ -2,15 +2,21 @@ version: '3.3' # 兼容旧版docker-compose
|
||||
services:
|
||||
certd:
|
||||
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号,例如:certd:1.29.0
|
||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
image: registry.cn-shenzhen.aliyuncs.com/certd/certd:latest # 稳定版为 certd/certd:stable
|
||||
# image: ghcr.io/certd/certd:latest # --------- 如果 报镜像not found,可以尝试其他镜像源
|
||||
# image: greper/certd:latest
|
||||
# image: certd/certd:latest
|
||||
# --------- 生产建议使用稳定版, latest改成stable即可
|
||||
# image: registry.cn-shenzhen.aliyuncs.com/certd/certd:stable
|
||||
|
||||
|
||||
# security_opt: # --------- 如果slim镜像下启动报错,尝试去掉这两行注释
|
||||
# - seccomp=unconfined # 解决slim镜像下WorkerThreadsTaskRunner::DelayedTaskScheduler::Start() 报错问题
|
||||
container_name: certd # 容器名
|
||||
restart: unless-stopped # 自动重启
|
||||
volumes:
|
||||
# ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】
|
||||
# 只要修改冒号前面的,冒号后面的/app/data不要动
|
||||
- /data/certd:/app/data
|
||||
- /data/certd:/app/data # 只要修改冒号前面的,冒号后面的/app/data切记切记不要动
|
||||
#- /volume1/docker/certd:/app/data:delegated #群晖使用这个配置
|
||||
# ↓↓↓↓↓ -------------------------------------------------------- 如果走时不准,考虑挂载localtime文件
|
||||
#- /etc/localtime:/etc/localtime
|
||||
#- /etc/timezone:/etc/timezone
|
||||
@@ -45,8 +51,10 @@ services:
|
||||
# 设置环境变量即可自定义certd配置
|
||||
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
|
||||
# 配置规则: certd_ + 配置项, 点号用_代替
|
||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false
|
||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,docker compose up -d 重建容器之后,管理员密码将改成123456,然后请及时修改回false
|
||||
- certd_system_resetAdminPasswd=false
|
||||
# ↓↓↓ 要使用ipv6,将此配置修改为::
|
||||
- certd_koa_hostname=0.0.0.0
|
||||
|
||||
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
|
||||
# 注意: 选定使用一种数据库之后,不支持更换数据库。
|
||||
@@ -60,7 +68,7 @@ services:
|
||||
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
|
||||
# - certd_typeorm_dataSource_default_database=certd # 数据库名
|
||||
|
||||
# #↓↓↓↓ ----------------------------- 使用mysql数据库,需要提前创建数据库 charset=utf8mb4, collation=utf8mb4_bin
|
||||
# #↓↓↓↓ ----------------------------- 使用mysql8数据库,需要提前创建数据库 charset=utf8mb4, collation=utf8mb4_bin
|
||||
# - certd_flyway_scriptDir=./db/migration-mysql # 升级脚本目录
|
||||
# - certd_typeorm_dataSource_default_type=mysql # 数据库类型, 或者 mariadb
|
||||
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
|
||||
@@ -69,7 +77,13 @@ services:
|
||||
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
|
||||
# - certd_typeorm_dataSource_default_database=certd # 数据库名
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
|
||||
# #↓↓↓↓ ---------------------------- 如果数据库服务端开启了加密传输 require_secure_transport-ON
|
||||
# - certd_typeorm_dataSource_default_ssl_rejectUnauthorized=false # 忽略证书校验,或者配置下面的三项路径
|
||||
# - certd_typeorm_dataSource_default_ssl_ca # 证书CA路径【如果上面false,无需配置】
|
||||
# - certd_typeorm_dataSource_default_ssl_key # 私钥路径【如果上面false,无需配置】
|
||||
# - certd_typeorm_dataSource_default_ssl_cert # 证书路径【如果上面false,无需配置】
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest / stable
|
||||
# certd-updater: # 添加 Watchtower 服务
|
||||
# image: containrrr/watchtower:latest
|
||||
# container_name: certd-updater
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CERTD_VERSION="${CERTD_VERSION:-latest}"
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/certd}"
|
||||
COMPOSE_FILE_URL="https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml"
|
||||
COMPOSE_FILE="$INSTALL_DIR/docker-compose.yaml"
|
||||
|
||||
DOCKER_MIRROR="https://mirrors.aliyun.com"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
check_command() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
get_local_ip() {
|
||||
LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K[^ ]+' | head -1)
|
||||
if [ -z "$LOCAL_IP" ]; then
|
||||
LOCAL_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
fi
|
||||
if [ -z "$LOCAL_IP" ]; then
|
||||
LOCAL_IP="127.0.0.1"
|
||||
fi
|
||||
echo "$LOCAL_IP"
|
||||
}
|
||||
|
||||
get_public_ip() {
|
||||
PUBLIC_IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null)
|
||||
if [ -z "$PUBLIC_IP" ]; then
|
||||
PUBLIC_IP=$(curl -s --max-time 5 https://checkip.amazonaws.com 2>/dev/null)
|
||||
fi
|
||||
if [ -z "$PUBLIC_IP" ]; then
|
||||
PUBLIC_IP=""
|
||||
fi
|
||||
echo "$PUBLIC_IP"
|
||||
}
|
||||
|
||||
show_access_urls() {
|
||||
LOCAL_IP=$(get_local_ip)
|
||||
PUBLIC_IP=$(get_public_ip)
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
log_info "安装完成!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "访问地址:"
|
||||
if [ -n "$PUBLIC_IP" ]; then
|
||||
echo -e " ${GREEN}外网访问:${NC} http://$PUBLIC_IP:7001"
|
||||
fi
|
||||
echo -e " ${GREEN}局域网:${NC} http://$LOCAL_IP:7001"
|
||||
echo ""
|
||||
echo "配置文件: $COMPOSE_FILE"
|
||||
echo ""
|
||||
echo "常用命令:"
|
||||
echo " cd $INSTALL_DIR"
|
||||
echo " docker compose logs -f # 查看日志"
|
||||
echo " docker compose restart # 重启服务"
|
||||
echo " docker compose down # 停止服务"
|
||||
echo ""
|
||||
}
|
||||
|
||||
detect_os() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
OS=$ID
|
||||
VER=$VERSION_ID
|
||||
elif [ -f /etc/centos-release ]; then
|
||||
OS="centos"
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
OS="rhel"
|
||||
else
|
||||
OS="unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
check_docker() {
|
||||
if check_command docker; then
|
||||
DOCKER_VERSION=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',')
|
||||
log_info "Docker 已安装: $DOCKER_VERSION"
|
||||
return 0
|
||||
else
|
||||
log_warn "Docker 未安装"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_docker_compose() {
|
||||
if check_command docker-compose; then
|
||||
COMPOSE_VERSION=$(docker-compose --version 2>/dev/null | awk '{print $3}' | tr -d ',')
|
||||
log_info "Docker Compose 已安装: $COMPOSE_VERSION"
|
||||
return 0
|
||||
elif docker compose version >/dev/null 2>&1; then
|
||||
log_info "Docker Compose (插件版) 已安装"
|
||||
return 0
|
||||
else
|
||||
log_warn "Docker Compose 未安装"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker_ubuntu() {
|
||||
log_info "正在安装 Docker (Ubuntu/Debian)..."
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl gnupg lsb-release
|
||||
|
||||
mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/${OS}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || \
|
||||
curl -fsSL https://download.docker.com/linux/${OS}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/${OS} $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
apt-get update
|
||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Docker 安装完成"
|
||||
}
|
||||
|
||||
install_docker_centos() {
|
||||
log_info "正在安装 Docker (CentOS/RHEL)..."
|
||||
yum install -y yum-utils
|
||||
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
|
||||
yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Docker 安装完成"
|
||||
}
|
||||
|
||||
install_dockerrocky() {
|
||||
log_info "正在安装 Docker (Rocky Linux/AlmaLinux)..."
|
||||
dnf install -y yum-utils
|
||||
dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Docker 安装完成"
|
||||
}
|
||||
|
||||
install_docker_debian() {
|
||||
log_info "正在安装 Docker (Debian)..."
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl gnupg2
|
||||
|
||||
mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | gpg --armor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || \
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --armor -o /etc/apt/keyrings/docker.gpg
|
||||
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list
|
||||
|
||||
apt-get update
|
||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
log_info "Docker 安装完成"
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
detect_os
|
||||
log_info "检测到操作系统: $OS"
|
||||
|
||||
case $OS in
|
||||
ubuntu)
|
||||
install_docker_ubuntu
|
||||
;;
|
||||
debian)
|
||||
install_docker_debian
|
||||
;;
|
||||
centos)
|
||||
install_docker_centos
|
||||
;;
|
||||
rhel|rocky|almalinux)
|
||||
install_dockerrocky
|
||||
;;
|
||||
*)
|
||||
log_error "不支持的操作系统: $OS"
|
||||
log_info "请手动安装 Docker"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_docker_compose_standalone() {
|
||||
log_info "正在安装 Docker Compose (独立版本)..."
|
||||
|
||||
COMPOSE_URLS=(
|
||||
"https://get.daocloud.io/docker/compose/releases/download/v2.12.2/docker-compose-$(uname -s)-$(uname -m)"
|
||||
"https://mirror.sjtu.edu.cn/github/docker/compose/releases/download/v2.12.2/docker-compose-$(uname -s)-$(uname -m)"
|
||||
"https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-$(uname -s)-$(uname -m)"
|
||||
)
|
||||
|
||||
for url in "${COMPOSE_URLS[@]}"; do
|
||||
log_info "尝试从: $url"
|
||||
if curl -L "$url" -o /usr/local/bin/docker-compose 2>/dev/null; then
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
log_info "Docker Compose 安装完成"
|
||||
return 0
|
||||
fi
|
||||
log_warn "下载失败,尝试下一个源..."
|
||||
done
|
||||
|
||||
log_error "Docker Compose 安装失败"
|
||||
return 1
|
||||
}
|
||||
|
||||
install_docker_compose() {
|
||||
if check_command docker && docker compose version >/dev/null 2>&1; then
|
||||
log_info "Docker Compose 插件已可用"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if check_command docker-compose; then
|
||||
log_info "Docker Compose 独立版本已安装"
|
||||
return 0
|
||||
fi
|
||||
|
||||
install_docker_compose_standalone
|
||||
}
|
||||
|
||||
download_compose_file() {
|
||||
log_info "正在下载 docker-compose.yaml..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
if curl -fsSL "$COMPOSE_FILE_URL" -o "$COMPOSE_FILE.tmp"; then
|
||||
mv "$COMPOSE_FILE.tmp" "$COMPOSE_FILE"
|
||||
log_info "docker-compose.yaml 已下载到 $COMPOSE_FILE"
|
||||
|
||||
if [ "$CERTD_VERSION" != "latest" ]; then
|
||||
sed -i "s|certd:latest|certd:$CERTD_VERSION|g" "$COMPOSE_FILE"
|
||||
log_info "已修改镜像版本为: $CERTD_VERSION"
|
||||
fi
|
||||
else
|
||||
log_error "下载失败,请检查网络连接"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_certd() {
|
||||
log_info "正在启动 Certd 容器..."
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
if docker compose -f "$COMPOSE_FILE" up -d 2>/dev/null; then
|
||||
log_info "Certd 启动成功!"
|
||||
elif docker-compose -f "$COMPOSE_FILE" up -d; then
|
||||
log_info "Certd 启动成功!"
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
docker ps --filter "name=certd" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -v, --version VERSION 指定 Certd 版本 (默认: latest)"
|
||||
echo " -p, --path PATH 指定安装路径 (默认: /opt/certd)"
|
||||
echo " -h, --help 显示帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 使用默认配置安装"
|
||||
echo " $0 -v 1.29.0 # 安装指定版本"
|
||||
echo " $0 -p /data/certd # 安装到指定目录"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " Certd 一键安装脚本"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-v|--version)
|
||||
CERTD_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--path)
|
||||
INSTALL_DIR="$2"
|
||||
COMPOSE_FILE="$INSTALL_DIR/docker-compose.yaml"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log_info "Certd 版本: $CERTD_VERSION"
|
||||
log_info "安装路径: $INSTALL_DIR"
|
||||
echo ""
|
||||
|
||||
DOCKER_INSTALLED=true
|
||||
COMPOSE_INSTALLED=true
|
||||
|
||||
if ! check_docker; then
|
||||
echo ""
|
||||
log_info "正在安装 Docker..."
|
||||
install_docker
|
||||
fi
|
||||
|
||||
if ! check_docker_compose; then
|
||||
echo ""
|
||||
log_info "正在安装 Docker Compose..."
|
||||
install_docker_compose
|
||||
fi
|
||||
|
||||
download_compose_file
|
||||
start_certd
|
||||
|
||||
show_access_urls
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -81,7 +81,8 @@ export default defineConfig({
|
||||
]
|
||||
},
|
||||
{text: "演示教程", link: "/guide/tutorial.md"},
|
||||
{text: "版本升级", link: "/guide/install/upgrade.md"}
|
||||
{text: "版本升级", link: "/guide/install/upgrade.md"},
|
||||
{text: "赞助专业版", link: "/guide/donate/"},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -95,10 +96,10 @@ export default defineConfig({
|
||||
},
|
||||
{
|
||||
text: "插件列表", items: [
|
||||
{text: "授权提供商", link: "/guide/plugins/access.md"},
|
||||
{text: "DNS提供商", link: "/guide/plugins/dns-provider.md"},
|
||||
{text: "任务插件", link: "/guide/plugins/deploy.md"},
|
||||
{text: "通知插件", link: "/guide/plugins/notification.md"},
|
||||
{text: "授权提供商", link: "/guide/plugins/access.md"},
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -107,19 +108,26 @@ export default defineConfig({
|
||||
text: "常见问题",
|
||||
items: [
|
||||
{text: "QA", link: "/guide/qa/use.md"},
|
||||
{text: "常见报错处理", link: "/guide/qa/"},
|
||||
{text: "忘记密码/无法登录", link: "/guide/use/forgotpasswd/"},
|
||||
{text: "群晖证书部署", link: "/guide/use/synology/"},
|
||||
{text: "腾讯云密钥获取", link: "/guide/use/tencent/"},
|
||||
{text: "连接windows主机", link: "/guide/use/host/windows.md"},
|
||||
{text: "Google EAB获取", link: "/guide/use/google/"},
|
||||
{text: "阿里云相关", link: "/guide/use/aliyun/"},
|
||||
{text: "忘记密码", link: "/guide/use/forgotpasswd/"},
|
||||
{text: "Azure相关", link: "/guide/use/azure/dns.md"},
|
||||
{text: "数据备份", link: "/guide/use/backup/"},
|
||||
{text: "Certd本身的证书更新", link: "/guide/use/https/index.md"},
|
||||
{text: "js脚本插件使用", link: "/guide/use/custom-script/index.md"},
|
||||
{text: "邮箱配置", link: "/guide/use/email/index.md"},
|
||||
{text: "证书复用", link: "/guide/use/pretask/"},
|
||||
{text: "IPv6支持", link: "/guide/use/setting/ipv6.md"},
|
||||
{text: "ESXi", link: "/guide/use/ESXi/index.md"},
|
||||
{text: "宝塔动态IP白名单", link: "/guide/use/baota/white_list.md"},
|
||||
{text: "子域名托管", link: "/guide/use/cert/subdomain.md"},
|
||||
{text: "流水线有效期", link: "/guide/use/pipeline/valid.md"},
|
||||
{text: "IP证书申请", link: "/guide/use/cert/ip.md"},
|
||||
{text: "企业模式", link: "/guide/use/mode/enterprise.md"},
|
||||
{text: "插件开发", link: "/guide/use/dev/plugin.md"},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -127,6 +135,7 @@ export default defineConfig({
|
||||
{text: "支付宝配置", link: "/guide/use/comm/payments/alipay.md"},
|
||||
{text: "微信支付配置", link: "/guide/use/comm/payments/wxpay.md"},
|
||||
{text: "彩虹易支付配置", link: "/guide/use/comm/payments/yizhifu.md"},
|
||||
{text: "插件选项映射", link: "/guide/use/comm/plugin/"},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -136,7 +145,6 @@ export default defineConfig({
|
||||
{text: "更新日志", link: "/guide/changelogs/CHANGELOG.md"},
|
||||
{text: "镜像说明", link: "/guide/image.md"},
|
||||
{text: "联系我们", link: "/guide/contact/"},
|
||||
{text: "捐赠", link: "/guide/donate/"},
|
||||
{text: "开源协议", link: "/guide/license/"},
|
||||
{text: "我的其他开源项目", link: "/guide/link/"},
|
||||
]
|
||||
|
||||
@@ -39,13 +39,21 @@ pnpm install
|
||||
pnpm init
|
||||
```
|
||||
|
||||
### lib包编译:
|
||||
将packages下面依赖的包都编译一遍,并监听改动。
|
||||
```shell
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 启动 server:
|
||||
启动server
|
||||
```shell
|
||||
cd packages/ui/certd-server
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 启动 client:
|
||||
启动前端
|
||||
```shell
|
||||
cd packages/ui/certd-client
|
||||
pnpm dev
|
||||
|
||||
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 92 KiB |
@@ -1,28 +1,62 @@
|
||||
# 捐赠
|
||||
************************
|
||||
支持开源,为爱发电,我已入驻爱发电
|
||||
https://afdian.com/a/greper
|
||||
# 专业版赞助
|
||||
|
||||
## 发电权益:
|
||||
1. 可加入发电专属群,可以获得作者一对一技术支持
|
||||
## 开源为什么要做专业版收费?
|
||||
1. 纯靠为爱发电不可持续,容易烂尾(比如:我的[dev-sidecar项目](https://github.com/docmirror/dev-sidecar)即便是拥有20K+star,也差点凉凉,幸亏有另外大佬接手用爱发电)
|
||||
2. 没有赞助的项目,作者会比较任性,不会用心倾听用户的心声,不顾用户体验(比如:下意识拒绝需求、频繁破坏性变更升级、全盘推倒重来之类的)
|
||||
3. 没有赞助的项目,交流群的戾气有时候比较重,容易起冲突
|
||||
|
||||
## 赞助权益:
|
||||
1. 可加入专属VIP群,可以获得作者一对一技术支持,必要时可以远程协助
|
||||
2. 您的需求我们将优先实现,并且将作为专业版功能提供
|
||||
3. 一年期专业版激活码
|
||||
3. 获得专业版功能
|
||||
|
||||
****------------------****
|
||||
> [限时¥50永久专业版优惠券,点我立刻领取](https://app.handfree.work/subject/#/app/certd/product)
|
||||
|
||||
****------------------****
|
||||
## 专业版特权对比
|
||||
|
||||
| 功能 | 免费版 | 专业版 |
|
||||
|---------|------------------------|-----------------------------|
|
||||
| 免费证书申请 | 免费无限制 | 免费无限制 |
|
||||
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署等 | 支持群晖、宝塔、1Panel等,持续开发中 |
|
||||
| 证书流水线条数 | 无限制 | 无限制 |
|
||||
| 站点证书监控 | 限制1条 | 无限制 |
|
||||
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、飞书、anpush、server酱等 |
|
||||
| 功能 | 免费版 | 专业版 | 商业版 |
|
||||
|---------|---------------------------------------|--------------------------------|---------------------------------|
|
||||
| 证书申请 | 无限制 | 无限制 | 无限制 |
|
||||
| 证书域名数量 | 无限制 | 无限制 | 无限制 |
|
||||
| 证书流水线条数 | 无限制 | 无限制 | 无限制 |
|
||||
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 | 同专业版 |
|
||||
| 通知 | 邮件通知、自定义webhook | 企微、钉钉、飞书、anpush、server酱等 | 同专业版 |
|
||||
| 站点监控 | 限制1条 | 无限制 | 无限制 |
|
||||
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 | 同专业版 |
|
||||
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 | 商业版技术支持 |
|
||||
| 站点个性化 | 无 | 无 | 可自定义站点名称、Logo等,移除Certd元素,首页警告等 |
|
||||
| 套餐功能 | 无 | 无 | 支持配置套餐供用户购买 |
|
||||
| 数据统计 | 无 | 无 | 支持站点各类统计数据 |
|
||||
| 插件管理 | 无 | 无 | 支持公共EAB设置,插件选项配置 |
|
||||
| 是否可商用 | 不允许 | 不允许 | 可对外运营 |
|
||||
|
||||
|
||||
|
||||
## 专业版激活方式
|
||||
|
||||

|
||||
|
||||
发电后,在私信中获取激活码
|
||||
************************
|
||||
|
||||
## 相关问题
|
||||
|
||||
### 1. 购买后VIP状态或时长未更新
|
||||
系统管理-->账号绑定页面,打开一下即可自动更新VIP最新状态(如果未登录袖手账号需要先登录)
|
||||
|
||||

|
||||
|
||||
|
||||
### 2. 开发票
|
||||
联系我们(微信:xiaojunnuo),并提供支付金额
|
||||
|
||||
### 3. VIP是否可以迁移换绑服务器?
|
||||
可以的。
|
||||
* 方式1. 直接将备份数据在新服务器上还原即可(首次访问会提示您是否绑定新url,点击是即可)
|
||||
* 方式2. 如果旧站点数据丢失,您也可以部署一个新站点,然后在系统管理-->账号绑定页面,转移VIP即可
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
# 镜像说明
|
||||
|
||||
## 镜像版本说明
|
||||
|
||||
| 版本标签 | 基础系统 | OpenJDK | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `latest` / `[version]` | Alpine Linux | 有 | **推荐**,默认版本,镜像体积更小,功能完整,支持所有插件 |
|
||||
| `slim` / `[version]-slim` | Debian slim | 有 | glibc 兼容性更好,适用于依赖 glibc 的特殊场景 |
|
||||
| `armv7` / `[version]-armv7` | Alpine Linux | 有 | ARMv7 架构专用版本 |
|
||||
|
||||
## 国内镜像地址:
|
||||
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim`、`[version]-slim`
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7`、`[version]-armv7`
|
||||
|
||||
## DockerHub地址:
|
||||
* `https://hub.docker.com/r/greper/certd`
|
||||
* `greper/certd:latest`
|
||||
* `greper/certd:slim`、`greper/certd:[version]-slim`
|
||||
* `greper/certd:armv7`、`greper/certd:[version]-armv7`
|
||||
|
||||
## GitHub Packages地址:
|
||||
* `ghcr.io/certd/certd:latest`
|
||||
* `ghcr.io/certd/certd:slim`、`ghcr.io/certd/certd:[version]-slim`
|
||||
* `ghcr.io/certd/certd:armv7`、`ghcr.io/certd/certd:[version]-armv7`
|
||||
*
|
||||
|
||||
## 镜像构建公开
|
||||
镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||
镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||
* [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml)
|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -5,6 +5,18 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
||||
|
||||
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
|
||||
|
||||
|
||||
| |官方开源地址: |
|
||||
| ---- | ---- |
|
||||
| [Github](https://github.com/certd/certd)|  |
|
||||
| [Gitee](https://gitee.com/certd/certd) |  |
|
||||
| [AtomGit](https://atomgit.com/certd/certd) | |
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 1、关于证书续期
|
||||
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
|
||||
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
|
||||
@@ -15,7 +27,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书(支持所有注册商注册的域名,支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式)
|
||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等70+部署插件)
|
||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等100+部署插件)
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
|
||||
* 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
|
||||
* 私有化部署,数据保存本地,安装升级非常简单快捷
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"notice": "永久专业版上线,新用户立减50,升级到最新版点击下方“立即赞助”按钮前往获取",
|
||||
"plus": {
|
||||
"name": "专业版",
|
||||
"price": "89.9",
|
||||
"price3": "199",
|
||||
"tooltip": "开源需要您的赞助支持",
|
||||
"priceText":"¥89.9/年",
|
||||
"discountText":"永久专业版50优惠券立即领取"
|
||||
},
|
||||
"comm": {
|
||||
"name": "商业版",
|
||||
"price": "399",
|
||||
"price3": "899",
|
||||
"tooltip": "3年优惠300",
|
||||
"priceText":"¥399/年",
|
||||
"discountText":"¥899/3年(3年优惠300)"
|
||||
},
|
||||
"app":{
|
||||
"minVersion":"1.36.0",
|
||||
"minVersionTip":"版本过低,为了您的数据安全,请尽快升级"
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -7,10 +7,41 @@ https://1panel.cn/docs/installation/online_installation/
|
||||
|
||||
## 二、部署certd
|
||||
|
||||
有两种安装方式
|
||||
|
||||
### 1. 应用商店方式安装【推荐】
|
||||
|
||||
#### 1.1 安装
|
||||
打开`1Panel->应用商店`,更新远程应用,搜索`certd`,点击安装
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 1.2 访问测试:
|
||||
http://ip:7001
|
||||
https://ip:7002
|
||||
默认账号密码
|
||||
admin/123456
|
||||
登录后请及时修改密码
|
||||
|
||||
#### 1.3 备份
|
||||

|
||||
|
||||
#### 1.4 恢复
|
||||
安装新Certd后,点击导入备份按钮,选择上面备份的文件即可
|
||||
|
||||
|
||||
|
||||
### 2. docker-compose方式安装
|
||||
|
||||
#### 2.1 安装
|
||||
1. 打开`docker-compose.yaml`,整个内容复制下来
|
||||
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
|
||||
|
||||
::: tip
|
||||
默认使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](../database.md)
|
||||
:::
|
||||
|
||||
2. 然后到 `1Panel->容器->编排->新建编排`
|
||||
输入名称,粘贴`docker-compose.yaml`原文内容
|
||||
@@ -20,9 +51,12 @@ https://1panel.cn/docs/installation/online_installation/
|
||||

|
||||
|
||||
> 默认使用sqlite数据库,数据保存在`/data/certd`目录下,您可以手动备份该目录
|
||||
> certd还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
|
||||
|
||||
3. 访问测试
|
||||
|
||||
|
||||
|
||||
|
||||
#### 2.2 访问测试
|
||||
|
||||
http://ip:7001
|
||||
https://ip:7002
|
||||
@@ -30,7 +64,7 @@ https://ip:7002
|
||||
admin/123456
|
||||
登录后请及时修改密码
|
||||
|
||||
## 三、升级
|
||||
#### 2.3 升级
|
||||
|
||||
1. 找到容器,点击更多->升级
|
||||

|
||||
@@ -39,11 +73,11 @@ admin/123456
|
||||

|
||||
|
||||
|
||||
## 四、数据备份
|
||||
#### 2.4 备份
|
||||
|
||||
> 默认数据保存在`/data/certd`目录下,可以手动备份
|
||||
> 建议配置一条 [数据库备份流水线](../../use/backup/),自动备份
|
||||
|
||||
## 五、备份恢复
|
||||
#### 2.5 恢复
|
||||
|
||||
将备份的`db.sqlite`及同目录下的其他文件一起覆盖到原来的位置,重启certd即可
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
## 自动升级配置
|
||||
|
||||
### 1. 方法一:使用watchtower监控自动升级【推荐】
|
||||
|
||||
1. 修改docker-compose.yaml文件增加如下配置
|
||||
或 [下载完整的自动升级docker-compose.yaml配置](https://gitee.com/certd/certd/raw/v2/docker/auto/docker-compose.yaml)
|
||||
```yaml
|
||||
services:
|
||||
certd:
|
||||
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号 这里要保持为latest
|
||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
... # 这里是你原来的docker-compose.yaml配置
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 增加一个标签,表示certd需要自动升级
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: "true"
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级watchtower配置,注意:上面certd的版本号要保持为latest
|
||||
certd-updater: # 添加 Watchtower 服务
|
||||
image: containrrr/watchtower:latest
|
||||
container_name: certd-updater
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# 配置 自动更新
|
||||
environment:
|
||||
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
|
||||
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
|
||||
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
|
||||
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
|
||||
|
||||
```
|
||||
|
||||
2. 重启certd容器
|
||||
```shell
|
||||
cd certd
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
|
||||
### 2. 方法二:使用Certd版本监控功能【不太稳定】
|
||||
|
||||
1. 选择Github-检查Release版本插件
|
||||

|
||||
按如下图填写配置
|
||||

|
||||
|
||||
|
||||
2. 检测到新版本后执行宿主机升级命令:
|
||||
|
||||
```shell
|
||||
# 拉取最新镜像
|
||||
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
# 升级容器命令, 替换成你自己的certd更新命令
|
||||
export RESTART_CERT='sleep 10; cd ~/deploy/certd/ ; docker compose down; docker compose up -d'
|
||||
# 构造一个脚本10s后在后台执行,避免容器销毁时执行太快,导致流水线任务无法结束
|
||||
nohup sh -c '$RESTART_CERT' >/dev/null 2>&1 & echo '10秒后重启' && exit
|
||||
```
|
||||
@@ -21,30 +21,32 @@
|
||||
|
||||
#### 2.2 容器编排方式部署
|
||||
|
||||
1. 打开`docker-compose.yaml`,整个内容复制下来
|
||||
1. 打开`docker-compose.yaml`,整个内容复制下来
|
||||
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
|
||||
|
||||
|
||||
然后到宝塔里面进到docker->容器编排->添加容器编排
|
||||

|
||||
点击确定,等待启动完成
|
||||
然后到宝塔里面进到docker->容器编排->添加容器编排
|
||||

|
||||
点击确定,等待启动完成
|
||||

|
||||
|
||||
> certd默认使用sqlite数据库,另外支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
|
||||
::: tip
|
||||
默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](../database.md)
|
||||
:::
|
||||
|
||||
|
||||
## 二、访问应用
|
||||
|
||||
http://ip:7001
|
||||
https://ip:7002
|
||||
默认账号密码
|
||||
admin/123456
|
||||
http://ip:7001
|
||||
https://ip:7002
|
||||
默认账号密码
|
||||
admin/123456
|
||||
登录后请及时修改密码
|
||||
|
||||
## 三、如何升级
|
||||
宝塔升级certd非常简单
|
||||
|
||||
打开容器页面: `docker`->`容器编排`->`左侧选择Certd`->`更新镜像`
|
||||
打开容器页面: `docker`->`容器编排`->`左侧选择Certd`->`更新镜像`
|
||||
|
||||

|
||||
|
||||
@@ -80,5 +82,8 @@ admin/123456
|
||||
### 1. 无法访问Certd
|
||||
1. 确认服务器的安全规则,是否放开了对应端口
|
||||
2. 确认宝塔防火墙是否放开对应端口
|
||||
3. 尝试将Certd容器加入宝塔的`bridge`网络
|
||||

|
||||
3. 尝试将Certd容器加入宝塔的`bridge`网络
|
||||

|
||||
|
||||
### 2. 动态IP无法加白名单问题
|
||||
[Nginx代理解决方案](../../use/baota/white_list.md)
|
||||
@@ -65,9 +65,113 @@ docker-compose up -d
|
||||
|
||||
## 二、从旧版的sqlite切换数据库
|
||||
|
||||
1. 先将`旧certd`升级到最新版 (`建议:备份sqlite数据库` )
|
||||
2. 按照上面全新安装方式部署一套`新的certd` (`注意:新旧版本的certd要一致`)
|
||||
3. 使用数据库工具将数据从sqlite导入到mysql或postgresql (`注意:flyway_history数据表不要导入`)
|
||||
4. 重启新certd
|
||||
5. 确认没有问题之后,删除旧版certd
|
||||
从旧版`sqlite`迁移到`mysql`或`postgresql`数据库
|
||||
|
||||
下面以 `SQLite` 转 `MySQL` 为例进行演示
|
||||
|
||||

|
||||
|
||||
#### 0.前提条件:
|
||||
1. SQLite版Certd站点已经`升级到最新版` (`建议:备份sqlite数据库` )
|
||||
2. `全新安装`MySQL版本Certd(`确保是全新的,因为里面的数据会被清空覆盖`)
|
||||
3. 两套Certd站点版本一致
|
||||
|
||||
#### 1. 安装DBeaver工具
|
||||
|
||||
[https://dbeaver.io/download/](https://dbeaver.io/download/)
|
||||
|
||||

|
||||
|
||||
#### 2. 连接到sqlite数据库
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 3. 连接到mysql或postgresql数据库
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
#### 4. 开始同步数据
|
||||
|
||||
选择mysql数据库,选择所有的表(`flyway_history除外`),右键导入数据
|
||||
|
||||
> 切记flyway_history数据表不要导入
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
> 重要: 建议Fetch大小改成1000,避免内存溢出
|
||||
|
||||
下一步、下一步,直到数据加载设置,勾选`在加载前截断目标表`(此选项很重要,并且会清空mysql certd数据库中的数据)
|
||||

|
||||
> 重要: 在插入行后做提交,也改成1000,避免内存溢出
|
||||
|
||||
#### 5. 导入完成
|
||||
|
||||

|
||||
|
||||
#### 6. 重启MySQL版本Certd
|
||||
|
||||
访问MySQL版本测试,数据已成功迁移
|
||||
|
||||
确认没有问题之后,删除旧版certd
|
||||
|
||||
|
||||
## 三、问题
|
||||
|
||||
### 1. 迁移到PostgreSQL之后,所有表的Sequence会从1开始
|
||||
|
||||
执行如下sql 语句,批量将所有表的id自增设置到当前数据的最大值【仅迁移到postgresql需要执行】
|
||||
```sql
|
||||
DO $$
|
||||
DECLARE
|
||||
rec RECORD;
|
||||
seq_name TEXT;
|
||||
max_id BIGINT;
|
||||
BEGIN
|
||||
-- 遍历所有用户表(排除系统schema)
|
||||
FOR rec IN
|
||||
SELECT table_schema, table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_type = 'BASE TABLE'
|
||||
AND table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||
LOOP
|
||||
-- 检查表是否存在名为 'id' 的列
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = rec.table_schema
|
||||
AND table_name = rec.table_name
|
||||
AND column_name = 'id'
|
||||
) THEN
|
||||
-- 获取该列关联的序列名(如果是 serial 或 identity 列)
|
||||
seq_name := pg_get_serial_sequence(
|
||||
quote_ident(rec.table_schema) || '.' || quote_ident(rec.table_name),
|
||||
'id'
|
||||
);
|
||||
|
||||
IF seq_name IS NOT NULL THEN
|
||||
-- 查询当前表中 id 的最大值
|
||||
EXECUTE format('SELECT MAX(id) FROM %I.%I', rec.table_schema, rec.table_name) INTO max_id;
|
||||
|
||||
IF max_id IS NULL THEN
|
||||
-- 表为空,将序列重置为初始值 1(下次调用 nextval 返回 1)
|
||||
PERFORM setval(seq_name, 1, false);
|
||||
RAISE NOTICE '表 %.% 为空,序列 % 已重置为 1', rec.table_schema, rec.table_name, seq_name;
|
||||
ELSE
|
||||
-- 将序列当前值设置为最大值,下次调用 nextval 返回 max_id + 1
|
||||
PERFORM setval(seq_name, max_id, true);
|
||||
RAISE NOTICE '表 %.% 的序列 % 已设置为 %', rec.table_schema, rec.table_name, seq_name, max_id;
|
||||
END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
```
|
||||
@@ -2,7 +2,67 @@
|
||||
|
||||
## 一、安装
|
||||
|
||||
### 1. 环境准备
|
||||
### 镜像版本选择
|
||||
|
||||
Certd 提供多种 Docker 镜像版本,您可以根据需要选择:
|
||||
|
||||
#### 1. 镜像地址格式:
|
||||
|
||||
```
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:[version-][system-][latest/stable]
|
||||
------------ ↑ 镜像地址 ------------- ↑ 镜像名 -- ↑指定版本- ↑基础系统- ↑最新版本类型
|
||||
```
|
||||
#### 2. 版本标签:
|
||||
|
||||
**最新版本标签:**
|
||||
|
||||
| 版本 | 标签 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 最新预览版【默认】 | `certd:latest` | 指向最新开发版本,包含最新功能,但稳定性不如稳定版 |
|
||||
| 最新稳定版 | `certd:stable` | 指向经过充分测试的生产就绪版本,推荐生产环境使用 |
|
||||
|
||||
**系统分支版本:**
|
||||
|
||||
> 根据基础镜像不同,分为如下三个分支版本,没有特殊需求选择默认的即可(他们功能是一样的)
|
||||
|
||||
| 系统版本 | 版本标签 | 基础系统 | 说明 | 稳定版标签 | 指定版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| alpine【默认】 | `certd:latest` | Alpine Linux | 默认版本,镜像体积小,支持x86、ARM架构 | `certd:stable` | `certd:1.43.0` |
|
||||
| slim | `certd:slim` | Debian slim | 基于glibc,dns解析兼容性好 | `certd:slim-stable` | `certd:1.43.0-slim` |
|
||||
| armv7 | `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:armv7-stable` | `certd:1.43.0-armv7` |
|
||||
|
||||
#### 3. 镜像地址:
|
||||
|
||||
| 镜像仓库 | 最新预览版 | slim版 | armv7版 |
|
||||
| --- | --- | --- | --- |
|
||||
| 阿里云【默认】 | `registry.cn-shenzhen.aliyuncs.com/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
| Docker Hub | `certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
|
||||
> 1. 后面的各个版本省略了镜像地址,使用时需要将镜像地址拼接完整。
|
||||
> 2. 如果您不确定使用哪个版本,请使用默认的 `certd:latest` 版本。
|
||||
|
||||
最新版本号:
|
||||

|
||||
|
||||
|
||||
### 一键脚本安装(推荐)
|
||||
|
||||
如果您的服务器未安装 Docker,该脚本会自动为您安装 Docker 和 Docker Compose,并启动 Certd 容器。
|
||||
|
||||
```bash
|
||||
curl -fsSL https://gitee.com/certd/certd/raw/v2/docker/run/install.sh | bash
|
||||
```
|
||||
|
||||
> 支持 Ubuntu、Debian、CentOS、Rocky Linux、AlmaLinux 等主流发行版。
|
||||
> docker-compose文件目录:`/opt/certd` ,升级时需要先进入此目录
|
||||
> 运行时数据默认保存路径:`/data/certd` ,可使用参数指定:`-p /data/certd`
|
||||
|
||||
|
||||
|
||||
### 手动安装
|
||||
|
||||
#### 1. 环境准备
|
||||
|
||||
1.1 准备一台云服务器
|
||||
|
||||
@@ -19,9 +79,9 @@ https://docs.docker.com/engine/install/
|
||||
|
||||
```bash
|
||||
# 随便创建一个目录
|
||||
mkdir certd
|
||||
mkdir /opt/certd
|
||||
# 进入目录
|
||||
cd certd
|
||||
cd /opt/certd
|
||||
# 下载docker-compose.yaml文件,或者手动下载放到certd目录下
|
||||
wget https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
|
||||
|
||||
@@ -42,8 +102,9 @@ docker compose up -d
|
||||
> 如果提示 没有docker compose命令,请安装docker-compose
|
||||
> https://docs.docker.com/compose/install/linux/
|
||||
|
||||
> certd默认使用sqlite数据库,另外还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
|
||||
|
||||
::: tip
|
||||
默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](../database.md)
|
||||
:::
|
||||
|
||||
### 3. 访问测试
|
||||
|
||||
@@ -53,10 +114,14 @@ https://your_server_ip:7002
|
||||
记得修改密码
|
||||
|
||||
|
||||
## 二、升级
|
||||
## 二、升级Certd
|
||||
|
||||
::: warning
|
||||
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||
```
|
||||
# 查看/opt/certd/docker-compose.yaml配置
|
||||
- /data/certd:/app/data # 请务必确保 /app/data 这个路径没有改动,固定写死
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
@@ -66,6 +131,7 @@ https://your_server_ip:7002
|
||||
|
||||
### 如果使用`latest`版本
|
||||
```shell
|
||||
cd /opt/certd
|
||||
#重新拉取镜像
|
||||
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
# 重新启动容器
|
||||
|
||||
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 145 KiB |
@@ -11,8 +11,11 @@
|
||||
git clone https://github.com/certd/certd --depth=1
|
||||
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时,可以尝试此命令,1.x.x换成最新版本号
|
||||
cd certd
|
||||
|
||||
# 启动服务
|
||||
./start.sh
|
||||
./start.sh
|
||||
|
||||
|
||||
|
||||
```
|
||||
>如果是windows,请先安装`git for windows` ,然后右键,选择`open git bash here`打开终端,再执行`./start.sh`命令
|
||||
@@ -21,9 +24,9 @@ cd certd
|
||||
|
||||
### 访问测试
|
||||
|
||||
http://your_server_ip:7001
|
||||
https://your_server_ip:7002
|
||||
默认账号密码:admin/123456
|
||||
http://your_server_ip:7001
|
||||
https://your_server_ip:7002
|
||||
默认账号密码:admin/123456
|
||||
记得修改密码
|
||||
|
||||
|
||||
@@ -37,7 +40,7 @@ cp -rf ./packages/ui/certd-server/data ../certd-data-backup
|
||||
|
||||
git pull
|
||||
# 如果提示pull失败,可以尝试强制更新
|
||||
# git checkout v2 -f && git pull
|
||||
# git checkout v2 -f && git pull
|
||||
|
||||
# 先停止旧的服务,7001是certd的默认端口
|
||||
kill -9 $(lsof -t -i:7001)
|
||||
@@ -45,16 +48,31 @@ kill -9 $(lsof -t -i:7001)
|
||||
./start.sh
|
||||
|
||||
```
|
||||
::: warning
|
||||
升级certd版本前,切记切记先备份一下数据
|
||||
::: warning
|
||||
升级certd版本前,切记切记先备份一下数据
|
||||
:::
|
||||
|
||||
|
||||
## 三、数据备份
|
||||
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
|
||||
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
|
||||
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
|
||||
|
||||
|
||||
## 四、备份恢复
|
||||
|
||||
将备份的`db.sqlite`及同目录下的其他文件覆盖到原来的位置,重启certd即可
|
||||
|
||||
## 六、常见问题
|
||||
|
||||
### 1. npm install better-sqlite3 时,提示node-gyp需要vscode环境编译
|
||||
|
||||
1. 首先确保node版本为22以上
|
||||
2. 将下面两行加到 ~/.npmrc 里面
|
||||
3. 重新install
|
||||
> better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
> better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,57 +10,15 @@
|
||||
|
||||
::: warning
|
||||
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||
很多人docker不太会配置,数据目录没有映射出来,升级导致数据丢失
|
||||
```
|
||||
# docker-compose.yaml配置
|
||||
- /data/certd:/app/data # 请务必确保 /app/data 这个路径没有改动,固定写死
|
||||
```
|
||||
具体备份方法可以参考上面每种部署方式升级方法后面的备份章节
|
||||
:::
|
||||
|
||||
## 升级日志
|
||||
可以查看最新版本号,以及所有版本的更新日志
|
||||
[CHANGELOG](../changelogs/CHANGELOG.md)
|
||||
|
||||
|
||||
## 自动升级配置
|
||||
|
||||
### 1. 方法一:使用watchtower监控
|
||||
|
||||
修改docker-compose.yaml文件增加如下配置, 使用watchtower监控自动升级
|
||||
```yaml
|
||||
services:
|
||||
certd:
|
||||
...
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: "true"
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
|
||||
certd-updater: # 添加 Watchtower 服务
|
||||
image: containrrr/watchtower:latest
|
||||
container_name: certd-updater
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# 配置 自动更新
|
||||
environment:
|
||||
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
|
||||
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
|
||||
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
|
||||
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 2. 方法二:使用Certd版本监控功能
|
||||
|
||||
选择Github-检查Release版本插件
|
||||

|
||||
按如下图填写配置
|
||||

|
||||
|
||||
|
||||
检测到新版本后执行宿主机升级命令:
|
||||
|
||||
```shell
|
||||
# 拉取最新镜像
|
||||
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
# 升级容器命令, 替换成你自己的certd更新命令
|
||||
export RESTART_CERT='sleep 10; cd ~/deploy/certd/ ; docker compose down; docker compose up -d'
|
||||
# 构造一个脚本10s后在后台执行,避免容器销毁时执行太快,导致流水线任务无法结束
|
||||
nohup sh -c '$RESTART_CERT' >/dev/null 2>&1 & echo '10秒后重启' && exit
|
||||
```
|
||||
@@ -5,11 +5,20 @@
|
||||
|
||||

|
||||
|
||||
:::tip
|
||||
|
||||
接口key分两种权限范围:
|
||||
1. 仅开放接口: 仅能访问下面`接口文档`中的接口
|
||||
2. 用户级别: 可访问Certd所有接口,没有文档,可以在浏览器中F12抓取网络请求参考
|
||||
|
||||
:::
|
||||
|
||||
## 接口文档
|
||||
|
||||
https://apifox.com/apidoc/shared-2e76f8c4-7c58-413b-a32d-a1316529af44/254949529e0
|
||||
|
||||
## Token生成方法
|
||||
|
||||
### Token生成方法
|
||||
|
||||
header中传入x-certd-token即可调用开放接口
|
||||
1、首先从OpenKey页面生成keyId,keySecret;
|
||||
@@ -17,14 +26,36 @@ header中传入x-certd-token即可调用开放接口
|
||||
3、将content加上keySecret进行签名: sign = md5(content + keySecret)
|
||||
4、然后将content和sign分别base64后用.号连接: x-certd-token = base64(content) +"."+base64(sign)
|
||||
|
||||
## SDK
|
||||
|
||||
### 参数
|
||||
支持证书id和域名两种方式获取证书。
|
||||
|
||||
### 创建新的证书申请
|
||||
参数`autoApply=true`将在没有证书时自动触发申请证书。申请参数支持另外传入:
|
||||
- `autoApplyTemplateId`:使用指定 ID 的证书申请参数模版;不传时不使用模版
|
||||
- `autoApplyParams`:自定义证书申请参数,会与系统默认参数、模版参数合并,并覆盖同名字段
|
||||
|
||||
检查逻辑如下:
|
||||
1. 如果证书仓库里面有,且没有过期,就直接返回证书
|
||||
2. 如果没有或者已过期,就会去找流水线,有就触发流水线执行
|
||||
3. 如果没有流水线,就创建一个流水线,触发运行(`注意:需要提前在域名管理中配置好域名校验方式,否则会申请失败`)
|
||||
4. 再次采用相同参数请求接口,如果在申请过程中,就会提示`正在申请中`,可轮循获取状态,直到证书申请成功。
|
||||
|
||||
|
||||
### SDK
|
||||
待开发
|
||||
|
||||
## 客户端工具
|
||||
|
||||
### SSL-Assistant
|
||||
### 官方客户端:Certd Client
|
||||
`Certd Client` 是 Certd 的官方证书部署客户端。运行在应用服务器上,自动发现本机 Nginx、Apache 和 IIS 站点,然后从 Certd 获取新证书并部署到本机。适用于不希望服务器开放 SSH、无法由 Certd 直接访问目标服务器的场景。
|
||||
|
||||
项目主页:[AtomGit](https://atomgit.com/certd/certd-client/) | [GitHub](https://github.com/certd/certd-client/)
|
||||
下载地址:[AtomGit Releases](https://atomgit.com/certd/certd-client/releases) | [GitHub Releases](https://github.com/certd/certd-client/releases)
|
||||
|
||||
### 第三方客户端:SSL-Assistant
|
||||
`SSL Assistant` 是一个基于 Go 语言开发的跨平台证书部署管理助手。
|
||||
支持自动扫描主机`Nginx`配置,然后从Certd拉取证书并部署。
|
||||
在不想暴露ssh主机密码情况下,该工具非常好用。
|
||||
|
||||
开源地址: https://github.com/Youngxj/SSL-Assistant
|
||||
开源地址: https://github.com/Youngxj/SSL-Assistant
|
||||
|
||||
@@ -2,50 +2,86 @@
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **阿里云授权** | |
|
||||
| 2.| **EAB授权** | ZeroSSL证书申请需要EAB授权 |
|
||||
| 3.| **google cloud** | 谷歌云授权 |
|
||||
| 4.| **主机登录授权** | |
|
||||
| 5.| **SFTP授权** | |
|
||||
| 6.| **阿里云OSS授权** | 包含地域和Bucket |
|
||||
| 7.| **FTP授权** | |
|
||||
| 8.| **腾讯云** | |
|
||||
| 9.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 10.| **七牛云授权** | |
|
||||
| 11.| **七牛OSS授权** | |
|
||||
| 12.| **天翼云授权** | |
|
||||
| 13.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 14.| **baota授权** | |
|
||||
| 15.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 16.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 17.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 18.| **cdnfly授权** | |
|
||||
| 19.| **群晖登录授权** | |
|
||||
| 20.| **k8s授权** | |
|
||||
| 21.| **1panel授权** | 账号和密码 |
|
||||
| 22.| **百度云授权** | |
|
||||
| 23.| **LeCDN授权** | |
|
||||
| 24.| **白山云授权** | |
|
||||
| 25.| **plesk授权** | |
|
||||
| 26.| **易支付** | |
|
||||
| 27.| **支付宝** | |
|
||||
| 28.| **微信支付** | |
|
||||
| 29.| **长亭雷池授权** | |
|
||||
| 30.| **lucky** | |
|
||||
| 31.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 32.| **uniCloud** | unicloud授权 |
|
||||
| 33.| **华为云授权** | |
|
||||
| 34.| **西部数码授权** | |
|
||||
| 35.| **多吉云** | |
|
||||
| 36.| **我爱云授权** | 我爱云CDN |
|
||||
| 37.| **CacheFly** | CacheFly |
|
||||
| 38.| **Gcore** | Gcore |
|
||||
| 39.| **亚马逊云aws授权** | |
|
||||
| 40.| **dns.la授权** | |
|
||||
| 41.| **又拍云** | |
|
||||
| 42.| **火山引擎** | |
|
||||
| 43.| **京东云** | |
|
||||
| 44.| **51dns授权** | |
|
||||
| 1.| **主机登录授权** | |
|
||||
| 2.| **阿里云授权** | |
|
||||
| 3.| **阿里云ESA授权** | |
|
||||
| 4.| **华为云授权** | |
|
||||
| 5.| **腾讯云** | |
|
||||
| 6.| **京东云** | |
|
||||
| 7.| **火山引擎** | |
|
||||
| 8.| **七牛云授权** | |
|
||||
| 9.| **百度云授权** | |
|
||||
| 10.| **baota授权** | |
|
||||
| 11.| **天翼云授权** | |
|
||||
| 12.| **51dns授权** | |
|
||||
| 13.| **AcePanel授权** | |
|
||||
| 14.| **SFTP授权** | |
|
||||
| 15.| **阿里云OSS授权** | 包含地域和Bucket |
|
||||
| 16.| **APISIX授权** | |
|
||||
| 17.| **橙域网络(asia-isp)授权** | 橙域网络CDN API授权,用于部署证书到橙域CDN |
|
||||
| 18.| **亚马逊云aws授权** | |
|
||||
| 19.| **亚马逊云科技(国区)授权** | |
|
||||
| 20.| **微软云Azure授权** | |
|
||||
| 21.| **BIND9 DNS 授权** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
|
||||
| 22.| **CacheFly** | CacheFly |
|
||||
| 23.| **ACME账号** | 用于复用ACME账号私钥和账号地址,证书申请时不再临时创建账号 |
|
||||
| 24.| **EAB授权** | ZeroSSL证书申请需要EAB授权 |
|
||||
| 25.| **google cloud** | 谷歌云授权 |
|
||||
| 26.| **cloudflare授权** | |
|
||||
| 27.| **中国移动CND授权** | |
|
||||
| 28.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 |
|
||||
| 29.| **dns.la授权** | |
|
||||
| 30.| **彩虹DNS** | 彩虹DNS管理系统授权 |
|
||||
| 31.| **多吉云** | |
|
||||
| 32.| **Dokploy授权** | |
|
||||
| 33.| **Dynadot授权** | |
|
||||
| 34.| **farcdn授权** | |
|
||||
| 35.| **FlexCDN授权** | |
|
||||
| 36.| **Gcore** | Gcore |
|
||||
| 37.| **Github授权** | |
|
||||
| 38.| **godaddy授权** | |
|
||||
| 39.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
|
||||
| 40.| **金山云授权** | |
|
||||
| 41.| **FTP授权** | |
|
||||
| 42.| **七牛OSS授权** | |
|
||||
| 43.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 44.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 45.| **namesilo授权** | |
|
||||
| 46.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||
| 47.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
|
||||
| 48.| **1panel授权** | 账号和密码 |
|
||||
| 49.| **支付宝** | |
|
||||
| 50.| **白山云授权** | |
|
||||
| 51.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||
| 52.| **cdnfly授权** | |
|
||||
| 53.| **k8s授权** | |
|
||||
| 54.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 55.| **LeCDN授权** | |
|
||||
| 56.| **lucky** | |
|
||||
| 57.| **猫云授权** | |
|
||||
| 58.| **plesk授权** | |
|
||||
| 59.| **长亭雷池授权** | |
|
||||
| 60.| **群晖登录授权** | |
|
||||
| 61.| **uniCloud** | unicloud授权 |
|
||||
| 62.| **微信支付** | |
|
||||
| 63.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 64.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 65.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 66.| **易支付** | |
|
||||
| 67.| **proxmox** | |
|
||||
| 68.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
||||
| 69.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
|
||||
| 70.| **UCloud授权** | 优刻得授权 |
|
||||
| 71.| **又拍云** | |
|
||||
| 72.| **网宿授权** | |
|
||||
| 73.| **西部数码授权** | |
|
||||
| 74.| **我爱云授权** | 我爱云CDN |
|
||||
| 75.| **新网授权(代理方式)** | |
|
||||
| 76.| **新网授权** | |
|
||||
| 77.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||
| 78.| **Zenlayer授权** | Zenlayer授权 |
|
||||
| 79.| **GoEdge授权** | |
|
||||
| 80.| **雨云授权** | https://app.rainyun.com/ |
|
||||
|
||||
<style module>
|
||||
table th:first-of-type {
|
||||
|
||||
@@ -1,96 +1,146 @@
|
||||
# 任务插件
|
||||
共 `70` 款任务插件
|
||||
共 `135` 款任务插件
|
||||
## 1. 证书申请
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **证书申请(JS版)** | 免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||
| 2.| **证书申请(Lego)** | 支持海量DNS解析提供商,推荐使用,一样的免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||
| 3.| **商用证书托管** | 手动上传自定义证书后,自动部署(每次证书有更新,都需要手动上传一次) |
|
||||
| 1.| **吊销旧证书** | 新证书申请成功,并全部部署成功后,吊销本流水线产生的旧证书。(注意 如果本流水线证书还在别处有使用或手动部署,请慎重添加此吊销任务) |
|
||||
| 2.| **证书申请(JS版)** | 免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||
| 3.| **已有证书托管** | 手动上传自定义证书后,自动部署(每次证书有更新,都需要手动上传一次) |
|
||||
| 4.| **获取阿里云订阅证书** | 从阿里云拉取订阅模式的商用证书(支持 API 1.0 和 2.0) |
|
||||
| 5.| **证书申请(Lego)** | 支持海量DNS解析提供商,推荐使用,一样的免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||
## 2. 主机
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **FTP-上传证书到FTP** | 将证书上传到FTP服务器 |
|
||||
| 2.| **IIS-部署到IIS站点** | |
|
||||
| 3.| **主机-执行远程主机脚本命令** | 可以执行重启nginx等操作让证书生效 |
|
||||
| 4.| **主机-部署证书到SSH主机** | SFTP上传证书到主机,然后SSH执行部署脚本命令 |
|
||||
| 1.| **主机-复制到本机** | 【仅管理员使用】实际上是复制证书到docker容器内的某个路径,需要做目录映射到宿主机 |
|
||||
| 2.| **主机-执行远程主机脚本命令** | 可以执行重启nginx等操作让证书生效 |
|
||||
| 3.| **IIS-部署到IIS站点** | |
|
||||
| 4.| **上传证书到对象存储OSS** | 支持阿里云OSS、腾讯云COS、七牛云KODO、S3、MinIO、FTP、SFTP |
|
||||
| 5.| **主机-部署证书到SSH主机** | 上传证书到主机覆盖原来的证书文件,然后自动执行部署脚本命令使证书生效 |
|
||||
| 6.| **ESXi-部署证书到ESXi** | |
|
||||
| 7.| **FTP-上传证书到FTP** | 将证书上传到FTP服务器 |
|
||||
| 8.| **Openwrt-部署证书到Openwrt** | |
|
||||
## 3. CDN
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **易盾-部署到易盾DCDN** | 主要是防御,http://user.yiduncdn.com/ |
|
||||
| 2.| **易盾-部署到易盾RCDN** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 3.| **cdnfly-部署证书到cdnfly** | cdnfly |
|
||||
| 4.| **百度云-部署证书到CDN** | 部署到百度云CDN |
|
||||
| 5.| **LeCDN-更新证书** | |
|
||||
| 6.| **LeCDN-更新证书V2** | 支持新版本LeCDN |
|
||||
| 7.| **白山云-更新证书** | |
|
||||
| 8.| **天翼云-部署证书到CDN** | 部署证书到天翼云CDN和全站加速 |
|
||||
| 9.| **括彩云-部署到括彩云CDN** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 10.| **多吉云-部署到多吉云CDN** | |
|
||||
| 11.| **我爱云-部署证书到我爱云CDN** | 部署证书到我爱云CDN |
|
||||
| 12.| **CacheFly-部署证书到CacheFly** | 部署证书到 CacheFly |
|
||||
| 13.| **Gcore-部署证书到Gcore** | 仅上传 并不会部署到cdn |
|
||||
| 14.| **Gcore-刷新Gcore证书** | 刷新现有的证书 |
|
||||
| 15.| **又拍云-部署证书到CDN/USS** | 支持又拍云CDN,又拍云云存储USS |
|
||||
| 1.| **APISIX-更新证书** | 自动更新APISIX证书 |
|
||||
| 2.| **橙域网络-部署证书到CDN** | 部署证书到橙域网络(asia-isp) CDN加速域名 |
|
||||
| 3.| **CacheFly-部署证书到CacheFly** | 部署证书到 CacheFly |
|
||||
| 4.| **中国移动-部署证书到CDN** | 中国移动自动部署证书到CDN |
|
||||
| 5.| **多吉云-部署到多吉云CDN** | |
|
||||
| 6.| **farcdn-更新证书** | www.farcdn.net |
|
||||
| 7.| **FlexCDN-更新证书** | |
|
||||
| 8.| **Gcore-刷新Gcore证书** | 刷新现有的证书 |
|
||||
| 9.| **Gcore-部署证书到Gcore** | 仅上传 并不会部署到cdn |
|
||||
| 10.| **GoEdge-更新证书** | GoEdge |
|
||||
| 11.| **金山云-更新CDN证书** | 金山云自动更新CDN证书 |
|
||||
| 12.| **白山云-更新证书** | |
|
||||
| 13.| **cdnfly-部署证书到cdnfly** | cdnfly |
|
||||
| 14.| **天翼云-部署证书到CDN** | 部署证书到天翼云CDN和全站加速 |
|
||||
| 15.| **括彩云-部署到括彩云CDN** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 16.| **LeCDN-更新证书V2** | 支持新版本LeCDN |
|
||||
| 17.| **LeCDN-更新证书** | |
|
||||
| 18.| **Maoyun-更新猫云CDN证书** | |
|
||||
| 19.| **易盾-部署到易盾DCDN** | 主要是防御,http://user.yiduncdn.com/ |
|
||||
| 20.| **易盾-部署到易盾RCDN** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 21.| **雨云-更新证书** | app.rainyun.com |
|
||||
| 22.| **又拍云-部署证书到CDN/USS** | 支持又拍云CDN,又拍云云存储USS |
|
||||
| 23.| **网宿-更新证书** | 网宿证书自动更新 |
|
||||
| 24.| **西数-部署到虚拟主机** | 西部数码部署证书到虚拟主机 |
|
||||
| 25.| **我爱云-部署证书到我爱云CDN** | 部署证书到我爱云CDN |
|
||||
| 26.| **Zenlayer-刷新证书** | 刷新Zenlayer CDN证书 |
|
||||
## 4. 面板
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **宝塔-面板证书部署** | 部署宝塔面板本身的ssl证书 |
|
||||
| 2.| **宝塔-网站证书部署** | 部署宝塔管理的站点的ssl证书,目前支持网站站点、docker站点等 |
|
||||
| 3.| **群晖-部署证书到群晖面板** | Synology,支持6.x以上版本 |
|
||||
| 4.| **K8S-部署证书到Secret** | 部署证书到k8s的secret |
|
||||
| 5.| **K8S-Ingress 证书部署** | 部署证书到k8s的Ingress |
|
||||
| 6.| **1Panel-部署证书到1Panel** | 更新1Panel的证书 |
|
||||
| 7.| **Plesk-部署Plesk网站证书** | |
|
||||
| 8.| **雷池-更新证书** | 更新长亭雷池WAF的证书 |
|
||||
| 9.| **lucky-更新Lucky证书** | |
|
||||
| 10.| **uniCloud-部署到服务空间** | 部署到服务空间 |
|
||||
| 11.| **威联通-部署证书到威联通** | 部署证书到qnap |
|
||||
| 1.| **AcePanel-部署到网站** | 上传证书并部署到指定网站 |
|
||||
| 2.| **AcePanel-面板证书** | 部署AcePanel面板证书 |
|
||||
| 3.| **Dokploy-部署server证书** | 自动更新Dokploy server证书 |
|
||||
| 4.| **飞牛NAS-部署证书** | |
|
||||
| 5.| **NextTerminal-更新证书** | 更新 Next Terminal 证书 |
|
||||
| 6.| **Nginx Proxy Manager-部署到主机** | 上传自定义证书到 Nginx Proxy Manager,并绑定到所选主机。 |
|
||||
| 7.| **1Panel-部署面板证书** | 更新1Panel的面板证书 |
|
||||
| 8.| **1Panel-更新站点证书** | 更新1Panel的站点证书 |
|
||||
| 9.| **宝塔-删除过期证书** | 删除证书夹中过期证书 |
|
||||
| 10.| **宝塔-全自动部署** | 根据证书域名自动匹配宝塔站点,全自动部署SSL证书。新增加速域名自动感知,自动新增部署 |
|
||||
| 11.| **宝塔-WAF证书部署** | 部署宝塔云WAF/aaWAF |
|
||||
| 12.| **宝塔-面板证书部署** | 部署宝塔面板本身的ssl证书 |
|
||||
| 13.| **宝塔win-网站证书部署** | 部署到Windows版宝塔管理的站点的ssl证书 |
|
||||
| 14.| **宝塔-网站证书部署** | 部署宝塔管理的站点的ssl证书,目前支持宝塔网站站点、docker站点等。本插件也支持aaPanel。 |
|
||||
| 15.| **K8S-Apply自定义yaml** | apply自定义yaml到k8s |
|
||||
| 16.| **K8S-Ingress 证书部署** | 部署证书到k8s的Ingress |
|
||||
| 17.| **K8S-部署证书到Secret** | 部署证书到k8s的secret |
|
||||
| 18.| **lucky-更新Lucky证书** | |
|
||||
| 19.| **Plesk-部署Plesk网站证书** | |
|
||||
| 20.| **Plesk-更新证书** | 不会创建新证书记录,直接更新旧的证书 |
|
||||
| 21.| **雷池-更新证书(支持控制台和防护应用)** | 更新长亭雷池WAF的证书,支持更新控制台和防护应用的证书。 |
|
||||
| 22.| **群晖-部署证书到群晖面板** | Synology,支持6.x以上版本 |
|
||||
| 23.| **群晖-刷新OTP登录有效期** | 群晖登录状态可能30天失效,需要在失效之前登录一次,刷新有效期,您可以将其放在“部署到群晖面板”任务之后 |
|
||||
| 24.| **uniCloud-部署到服务空间** | 部署到服务空间 |
|
||||
| 25.| **Proxmox-上传证书到Proxmox** | |
|
||||
| 26.| **威联通-部署证书到威联通** | 部署证书到qnap |
|
||||
## 5. 阿里云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
|
||||
| 2.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数,支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
|
||||
| 3.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
|
||||
| 4.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务,自动部署域名证书至阿里云DCDN |
|
||||
| 5.| **阿里云-部署证书至OSS** | 自动部署域名证书至阿里云OSS |
|
||||
| 6.| **阿里云-上传证书到阿里云** | 如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出 |
|
||||
| 7.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
|
||||
| 8.| **阿里云-部署至ALB(应用负载均衡)** | ALB,更新监听器的默认证书 |
|
||||
| 9.| **阿里云-部署至NLB(网络负载均衡)** | NLB,网络负载均衡,更新监听器的默认证书 |
|
||||
| 10.| **阿里云-部署至SLB(传统负载均衡)** | 部署证书到阿里云SLB(传统负载均衡) |
|
||||
| 11.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算(FC3.0),【注意】证书的加密算法必须选择【pkcs1旧版】 |
|
||||
| 1.| **阿里云-删除即将过期证书** | 仅删除未使用的证书 |
|
||||
| 2.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
|
||||
| 3.| **阿里云-部署至ALB(应用负载均衡)** | ALB,更新监听器的默认证书 |
|
||||
| 4.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数,支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
|
||||
| 5.| **阿里云-部署至云原生API网关/AI网关** | 自动部署域名证书至云原生API网关、AI网关 |
|
||||
| 6.| **阿里云-部署证书至API网关** | 自动部署域名证书至阿里云API网关(APIGateway) |
|
||||
| 7.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
|
||||
| 8.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务,自动部署域名证书至阿里云DCDN |
|
||||
| 9.| **阿里云-部署至ESA** | 部署证书到阿里云ESA(边缘安全加速),支持边缘证书和SaaS证书两种模式 |
|
||||
| 10.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算(FC3.0) |
|
||||
| 11.| **阿里云-部署至GA** | 部署证书到阿里云GA(全球加速),支持更新默认证书和扩展证书 |
|
||||
| 12.| **阿里云-部署至直播(Live)** | 部署证书到阿里云视频直播(Live)域名 |
|
||||
| 13.| **阿里云-部署至NLB(网络负载均衡)** | NLB,网络负载均衡,更新监听器的默认证书 |
|
||||
| 14.| **阿里云-部署证书至OSS** | 部署域名证书至阿里云OSS自定义域名,不是上传到阿里云oss |
|
||||
| 15.| **阿里云-部署至CLB(传统负载均衡)** | 部署证书到阿里云CLB(传统负载均衡) |
|
||||
| 16.| **阿里云-部署至VOD** | 部署证书到阿里云视频点播(vod) |
|
||||
| 17.| **阿里云-部署至阿里云WAF(云产品接入)** | 部署证书到阿里云WAF(云产品接入),CNAME方式接入的请选择另外一个waf插件 |
|
||||
| 18.| **阿里云-部署至阿里云WAF(cname接入)** | 部署证书到阿里云WAF(cname接入),云资源的请选择另外一个waf插件 |
|
||||
| 19.| **阿里云-上传证书到CAS** | 上传证书到阿里云证书管理服务(CAS),如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出 |
|
||||
## 6. 华为云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **华为云-部署证书至CDN** | |
|
||||
| 2.| **华为云-部署证书至ELB负载均衡** | |
|
||||
| 3.| **华为云-部署证书至OBS** | |
|
||||
| 4.| **华为云-上传证书至CCM** | 上传证书到华为云云证书管理(CCM) |
|
||||
## 7. 腾讯云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **腾讯云-部署证书到任意云资源** | 支持负载均衡、CDN、DDoS、直播、点播、Web应用防火墙、API网关、TEO、容器服务、对象存储、轻应用服务器、云原生微服务、云开发 |
|
||||
| 2.| **腾讯云-部署到CLB** | 暂时只支持单向认证证书,暂时只支持通用负载均衡 |
|
||||
| 1.| **腾讯云-删除即将过期证书** | 仅删除即将过期且未使用的证书 |
|
||||
| 2.| **腾讯云-部署证书到任意云资源** | 支持负载均衡、CDN、DDoS、直播、点播、Web应用防火墙、API网关、TEO、容器服务、对象存储、轻应用服务器、云原生微服务、云开发 |
|
||||
| 3.| **腾讯云-部署到CDN(废弃)** | 已废弃,请使用v2版 |
|
||||
| 4.| **腾讯云-部署到CDN-v2** | 推荐使用 |
|
||||
| 5.| **腾讯云-上传证书到腾讯云** | 上传成功后输出:tencentCertId |
|
||||
| 6.| **腾讯云-部署证书到COS** | 部署到腾讯云COS源站域名证书【注意:很不稳定,需要重试很多次偶尔才能成功一次】 |
|
||||
| 7.| **腾讯云-部署到腾讯云EO** | 腾讯云边缘安全加速平台EO,必须配置上传证书到腾讯云任务 |
|
||||
| 8.| **腾讯云-删除即将过期证书** | 仅删除未使用的证书 |
|
||||
| 9.| **腾讯云-部署到TKE-ingress** | serverless集群请使用K8S部署插件;Qcloud类型需要【上传到腾讯云】作为前置任务;ApiServer未开启外网访问则需要做域名的内网IP映射 |
|
||||
| 4.| **腾讯云-部署到CDN-v2** | 推荐使用,支持CDN域名以及COS加速域名 |
|
||||
| 5.| **腾讯云-部署到CLB** | 暂时只支持单向认证证书,暂时只支持通用负载均衡 |
|
||||
| 6.| **腾讯云-部署证书到COS** | 部署到腾讯云COS源站域名证书,注意是源站域名,加速域名请使用腾讯云CDN v2插件【注意:很不稳定,需要重试很多次偶尔才能成功一次】 |
|
||||
| 7.| **腾讯云-部署到腾讯云EO** | 腾讯云边缘安全加速平台EdgeOne(EO) |
|
||||
| 8.| **腾讯云-部署到腾讯云直播** | https://console.cloud.tencent.com/live/ |
|
||||
| 9.| **腾讯云-部署到TKE** | 修改TKE集群密钥配置,支持Opaque和TLS证书类型。注意: 1. serverless集群请使用K8S部署插件; 2. Opaque类型需要【上传到腾讯云】作为前置任务; 3. ApiServer需要开通公网访问(或者certd可访问),实际上底层仍然是通过KubeClient进行部署 |
|
||||
| 10.| **腾讯云-更新证书(Id不变)** | 根据证书id一键更新腾讯云证书并自动部署(Id不变),注意:当前仅支持CLB,其他需要等腾讯接口完善 |
|
||||
| 11.| **腾讯云-实例开关机** | 腾讯云实例开关机 |
|
||||
| 12.| **腾讯云-上传证书到腾讯云** | 上传成功后输出:tencentCertId |
|
||||
## 8. 火山引擎
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **火山引擎-部署证书至CDN** | 支持网页,文件下载,音视频点播 |
|
||||
| 2.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
|
||||
| 3.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
||||
| 4.| **火山引擎-部署证书至ALB** | 部署至火山引擎应用负载均衡 |
|
||||
| 1.| **火山引擎-部署证书至ALB** | 部署至火山引擎应用负载均衡 |
|
||||
| 2.| **火山引擎-部署证书至CDN** | 支持网页,文件下载,音视频点播 |
|
||||
| 3.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
|
||||
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
|
||||
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
|
||||
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件 |
|
||||
| 7.| **火山引擎-替换VKE证书** | 替换火山引擎VKE集群中的TLS Secret证书 |
|
||||
| 8.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
|
||||
| 9.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
||||
## 9. 京东云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
@@ -98,26 +148,54 @@
|
||||
| 1.| **京东云-部署证书至CDN** | 京东云内容分发网络 |
|
||||
| 2.| **京东云-更新已有证书** | 更新SSL数字证书中的证书 |
|
||||
| 3.| **京东云-上传新证书** | 上传证书到SSL数字证书中心 |
|
||||
## 10. 七牛云
|
||||
## 10. UCloud
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **七牛云-部署证书至OSS** | 自动部署域名证书至七牛云KODO,注意是自定义源站域名,不是CDN域名 |
|
||||
| 2.| **七牛云-部署证书至CDN** | 自动部署域名证书至七牛云CDN |
|
||||
## 11. 亚马逊云
|
||||
| 1.| **UCloud-部署到CDN** | 将证书部署到UCloud CDN |
|
||||
| 2.| **UCloud-部署到负载均衡** | 将证书部署到UCloud负载均衡(ULB/ALB/CLB) |
|
||||
| 3.| **UCloud-部署到对象存储(US3)** | 将证书部署到UCloud对象存储(US3) |
|
||||
| 4.| **UCloud-部署到WAF** | 将证书部署到UCloud WAF |
|
||||
| 5.| **UCloud-上传到USSL** | 将证书上传到UCloud USSL |
|
||||
## 11. 百度云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **百度云-部署证书到负载均衡** | 部署到百度云负载均衡,包括BLB、APPBLB |
|
||||
| 2.| **百度云-部署到CCE** | 部署到百度云CCE集群Ingress等通过Secret管理证书的应用 |
|
||||
| 3.| **百度云-部署证书到CDN** | 部署到百度云CDN |
|
||||
| 4.| **百度云-上传到证书托管** | 上传证书到百度云证书托管中心 |
|
||||
## 12. 七牛云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **七牛云-部署证书至CDN/DCDN** | 自动部署域名证书至七牛云CDN、DCDN |
|
||||
| 2.| **七牛云-部署证书至OSS** | 自动部署域名证书至七牛云KODO,注意是自定义源站域名,不是CDN域名 |
|
||||
| 3.| **七牛云-上传证书到七牛云** | 上传到七牛云 |
|
||||
## 13. 亚马逊云
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **AWS-部署证书到CloudFront** | 部署证书到 AWS CloudFront |
|
||||
## 12. 其他
|
||||
| 2.| **AWS-上传证书到ACM** | 上传证书 AWS ACM |
|
||||
| 3.| **AWS(国区)-部署证书到CloudFront** | 部署证书到 AWS CloudFront |
|
||||
## 14. 其他
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **Demo-测试插件** | |
|
||||
| 2.| **重启 Certd** | 【仅管理员可用】 重启 certd的https服务,用于更新 Certd 的 ssl 证书 |
|
||||
| 3.| **自定义js脚本** | 【仅管理员】运行自定义js脚本执行 |
|
||||
| 2.| **Github-检查Release版本** | 检查最新Release版本并推送消息 |
|
||||
| 3.| **邮件发送证书** | 通过邮件发送证书 |
|
||||
| 4.| **等待** | 等待一段时间 |
|
||||
| 5.| **数据库备份** | 仅支持备份SQLite数据库 |
|
||||
| 5.| **webhook方式部署证书** | 调用webhook部署证书 |
|
||||
## 15. 管理
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|-----|-----|-----|
|
||||
| 1.| **数据库备份** | 【仅管理员可用】仅支持备份SQLite数据库 |
|
||||
| 2.| **重启 Certd** | 【仅管理员可用】 重启 certd的https服务,用于更新 Certd 的 ssl 证书 |
|
||||
| 3.| **部署证书到Certd本身** | 【仅管理员可用】 部署证书到 certd的https服务,用于更新 Certd 的 ssl 证书,建议将此任务放在流水线的最后一步 |
|
||||
| 4.| **自定义js脚本** | 【仅管理员】运行自定义js脚本执行 |
|
||||
|
||||
<style module>
|
||||
table th:first-of-type {
|
||||
|
||||