Compare commits
282 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d0b66c61 | ||
|
|
f3bbb9e8b2 | ||
|
|
dcd646b465 | ||
|
|
b122bbced9 | ||
|
|
5edae845cb | ||
|
|
b7af3c10e4 | ||
|
|
2453048288 | ||
|
|
221b6a6fc6 | ||
|
|
851ad70a6c | ||
|
|
1844e9a9db | ||
|
|
04e9f4e909 | ||
|
|
e6e2587bfc | ||
|
|
70bd2f0581 | ||
|
|
9e08cfd1d1 | ||
|
|
cd93a2d72c | ||
|
|
11a4d4f55c | ||
|
|
f33570d514 | ||
|
|
1ee3b64a19 | ||
|
|
a3a56f3346 | ||
|
|
564ed8f0d3 | ||
|
|
268ec4bd7f | ||
|
|
e55e6cc512 | ||
|
|
6c6bb78568 | ||
|
|
178c62512d | ||
|
|
9eaf9fd933 | ||
|
|
04abf9dd76 | ||
|
|
355059df3c | ||
|
|
4a6c32877f | ||
|
|
258f6b5001 | ||
|
|
78d9501fce | ||
|
|
15975bb92c | ||
|
|
fef8c3cb1a | ||
|
|
4b226d7730 | ||
|
|
41abc8cab8 | ||
|
|
4d3b3834f5 | ||
|
|
9b1ba574ff | ||
|
|
ee7d950c15 | ||
|
|
8ca41f18be | ||
|
|
9d522bd9ef | ||
|
|
f9633616e2 | ||
|
|
9a0dd53cd7 | ||
|
|
6bbcec6163 | ||
|
|
fdee69bdaf | ||
|
|
2ec4adf7d5 | ||
|
|
709684d00f | ||
|
|
989fd1ec6d | ||
|
|
7d57c5abc0 | ||
|
|
0c42bb845d | ||
|
|
07037e8d49 | ||
|
|
a1fc3841df | ||
|
|
1fee156457 | ||
|
|
82bdccf7e7 | ||
|
|
7936c34472 | ||
|
|
365fd0bd5b | ||
|
|
f63ee41405 | ||
|
|
26359b9d16 | ||
|
|
5abdb577fb | ||
|
|
c67c4741e5 | ||
|
|
60bc4be9ea | ||
|
|
b83f87b6c8 | ||
|
|
0a73ba1a53 | ||
|
|
a4d397f24b | ||
|
|
809f231981 | ||
|
|
1499c637ee | ||
|
|
e5805a028b | ||
|
|
5cb0463cf6 | ||
|
|
12c208cad4 | ||
|
|
1946a4e68a | ||
|
|
dfed0af053 | ||
|
|
fc064aa9f8 | ||
|
|
edaf08361f | ||
|
|
92c93822b9 | ||
|
|
18a19096d3 | ||
|
|
7e707cd973 | ||
|
|
d33b8caf14 | ||
|
|
e533f9407f | ||
|
|
193a19b79c | ||
|
|
609a252ee0 | ||
|
|
3be70c3696 | ||
|
|
3c2fbd720f | ||
|
|
11b413d0dc | ||
|
|
a117fd7d93 | ||
|
|
794695c313 | ||
|
|
7478dd7f47 | ||
|
|
2d17501072 | ||
|
|
034bb71b10 | ||
|
|
97f102533c | ||
|
|
a90b6a8589 | ||
|
|
e8f6c665f9 | ||
|
|
6dac89e9a1 | ||
|
|
4f512a6cdd | ||
|
|
94f162c189 | ||
|
|
f5807d215f | ||
|
|
3189e65bad | ||
|
|
3febc53061 | ||
|
|
efd8135341 | ||
|
|
56405eff6c | ||
|
|
e0fad9d9a9 | ||
|
|
8fe942d8d5 | ||
|
|
cf98e789d8 | ||
|
|
ff58b9a317 | ||
|
|
65e7d390b8 | ||
|
|
54ae378e30 | ||
|
|
347695cf66 | ||
|
|
8f4d854b0d | ||
|
|
94bd846726 | ||
|
|
0365841549 | ||
|
|
74bd1f64a0 | ||
|
|
5bce03410e | ||
|
|
32ff658e84 | ||
|
|
c10ceed753 | ||
|
|
eb45b56a87 | ||
|
|
283b150d60 | ||
|
|
7329a22132 | ||
|
|
50b48d956f | ||
|
|
55d7a05af8 | ||
|
|
6c70b0655a | ||
|
|
0004eac764 | ||
|
|
5fe24465d7 | ||
|
|
364ceb2399 | ||
|
|
88b90986b1 | ||
|
|
5143823e43 | ||
|
|
363e23dd13 | ||
|
|
44a6190e17 | ||
|
|
4475ed0dea | ||
|
|
6a23da3de3 | ||
|
|
0f1d5a7730 | ||
|
|
5b4c3bb668 | ||
|
|
ad49f9d788 | ||
|
|
397ceefa02 | ||
|
|
e11b1ca4e8 | ||
|
|
8e983e7286 | ||
|
|
f970ae7529 | ||
|
|
b0973b5ca8 | ||
|
|
4784bf9dba | ||
|
|
6b8dbf5235 | ||
|
|
48f698e84b | ||
|
|
ec0cdf8b96 | ||
|
|
2a6cc01eed | ||
|
|
acc1365101 | ||
|
|
c5409c78ba | ||
|
|
b97de6c06b | ||
|
|
abe755cb69 | ||
|
|
4e3f499d76 | ||
|
|
3cebe51796 | ||
|
|
25bd17dc6e | ||
|
|
2525f54dc3 | ||
|
|
2127bb7e69 | ||
|
|
ed6d74f1ba | ||
|
|
02dd11f196 | ||
|
|
37b9ae30e2 | ||
|
|
0463dbcc75 | ||
|
|
111ef97d9c | ||
|
|
e8e854e392 | ||
|
|
cedbaf70c0 | ||
|
|
ff43b9ab3e | ||
|
|
47c4ba9dd6 | ||
|
|
2899aa1b19 | ||
|
|
ecff16c89d | ||
|
|
6ff738144a | ||
|
|
26028bb1eb | ||
|
|
eb4d5ddfd5 | ||
|
|
093ee006e4 | ||
|
|
9f8aa15af8 | ||
|
|
74d66a0131 | ||
|
|
626a86dea7 | ||
|
|
9ab029a296 | ||
|
|
8e1a81ae53 | ||
|
|
d76e1a3204 | ||
|
|
b585782007 | ||
|
|
2d198bcef7 | ||
|
|
0edcd9174f | ||
|
|
daa5b44f8e | ||
|
|
949660bc01 | ||
|
|
899a0b75b0 | ||
|
|
8cdb2afa69 | ||
|
|
00ec2ce33e | ||
|
|
2f7fd95684 | ||
|
|
55b1794004 | ||
|
|
e20972d4e7 | ||
|
|
749d727f50 | ||
|
|
9b524728c0 | ||
|
|
f81b4b9680 | ||
|
|
d2eaea7a44 | ||
|
|
f77c2dae23 | ||
|
|
a72737fdd5 | ||
|
|
4ab6b72e6f | ||
|
|
1468e74a6c | ||
|
|
09b5a21af1 | ||
|
|
6ad0d8e42f | ||
|
|
deb3b2f412 | ||
|
|
893391a3d1 | ||
|
|
7503d52857 | ||
|
|
fb860981d6 | ||
|
|
f302c7fb74 | ||
|
|
a8be2a77cf | ||
|
|
c2345e6118 | ||
|
|
539f8f3343 | ||
|
|
9a06c1e35b | ||
|
|
382de0d6d6 | ||
|
|
4883b3bb88 | ||
|
|
0a90523d61 | ||
|
|
fa63f2a838 | ||
|
|
fd8ac3ae37 | ||
|
|
51c1b193e5 | ||
|
|
ee99bcf8a1 | ||
|
|
324086ca49 | ||
|
|
e9610eaede | ||
|
|
7d5c714211 | ||
|
|
24e275fdb3 | ||
|
|
597b9d0e17 | ||
|
|
4d710a1aaf | ||
|
|
5de033814b | ||
|
|
aaec840d8c | ||
|
|
e579cf6ceb | ||
|
|
798e72f663 | ||
|
|
e79d862256 | ||
|
|
53133db456 | ||
|
|
39f8484b2a | ||
|
|
892256c0b9 | ||
|
|
0545945697 | ||
|
|
ad0125fe0d | ||
|
|
fb325b5447 | ||
|
|
56ff9e6344 | ||
|
|
74b431481d | ||
|
|
12102ef641 | ||
|
|
445541c38f | ||
|
|
820f03e162 | ||
|
|
516a958c66 | ||
|
|
7da101d5b7 | ||
|
|
9667f3309b | ||
|
|
82735f3c02 | ||
|
|
752acb591f | ||
|
|
43d851c7ef | ||
|
|
95e1fc6b5f | ||
|
|
a8a12a3b91 | ||
|
|
63a95723ac | ||
|
|
02f806ab99 | ||
|
|
da6526d5fa | ||
|
|
347d166250 | ||
|
|
ef22d9d07b | ||
|
|
e4fd1e78f5 | ||
|
|
4acbbf6e13 | ||
|
|
16f20dc01d | ||
|
|
8e4b3d12bd | ||
|
|
09b1bf6e2d | ||
|
|
7e4aa24459 | ||
|
|
7c94999efc | ||
|
|
e27d4f11ee | ||
|
|
914c5b4870 | ||
|
|
882f802585 | ||
|
|
5579780b12 | ||
|
|
fd6e41c566 | ||
|
|
984d2a47b8 | ||
|
|
92bae0c439 | ||
|
|
af5d7465a1 | ||
|
|
b620052b88 | ||
|
|
c13a7a7873 | ||
|
|
65b199d392 | ||
|
|
dc0b86281e | ||
|
|
b8796991b5 | ||
|
|
83447fff62 | ||
|
|
4a02c252d5 | ||
|
|
cb88df04b0 | ||
|
|
e888df2b9f | ||
|
|
17af07e4bb | ||
|
|
d1aed36154 | ||
|
|
eb97f7a661 | ||
|
|
be822ccc93 | ||
|
|
e2b52eed61 | ||
|
|
21717985ac | ||
|
|
3c4ffee7d3 | ||
|
|
3e1a457609 | ||
|
|
b28f0dc5e4 | ||
|
|
29561ed75d | ||
|
|
2e931d1f67 | ||
|
|
c907f22275 | ||
|
|
19ccac5c05 | ||
|
|
f9e3797cdd | ||
|
|
a30379bfdb | ||
|
|
643e09a4e6 | ||
|
|
56fc2d8b44 |
@@ -10,5 +10,7 @@ trim_trailing_whitespace = true
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[*.go]
|
[*.go]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|||||||
80
.github/ISSUE_TEMPLATE/1-bug_report.yml
vendored
Normal file
80
.github/ISSUE_TEMPLATE/1-bug_report.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
name: "🐞 Bug Report"
|
||||||
|
description: "报告缺陷来帮助我们完善。 / Create a report to help us improve."
|
||||||
|
title: "[Bug] 简要描述你发现的缺陷"
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Welcome!
|
||||||
|
|
||||||
|
**在提交 Issue 之前,请确认以下事项**:
|
||||||
|
1. 我**确认**已尝试过使用当前最新版本,并能复现问题。由于开发者精力有限,非当前最新版本的问题将被直接关闭,感谢理解。
|
||||||
|
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
|
||||||
|
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
|
||||||
|
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
|
||||||
|
5. 请保持每个 Issue 只包含一个缺陷报告。如果有多个缺陷,请分别提交 Issue。
|
||||||
|
|
||||||
|
**Before you submit the issue, please make sure of the following checklist**:
|
||||||
|
1. Yes, I'm using the latest release and can reproduce the issue. Issues that are not in the latest version will be closed directly.
|
||||||
|
2. Yes, I've searched for [existing issues](https://github.com/usual2970/certimate/issues) (including closed ones) on GitHub and didn't find any similar.
|
||||||
|
3. Yes, I've read the [documentation](https://docs.certimate.me/en/) and didn't find any similar.
|
||||||
|
4. Please describe the problem in detail according to the template specification, otherwise the issue will be closed directly.
|
||||||
|
5. Please limit one report per issue.
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: 软件版本 / Release Version
|
||||||
|
description: 请提供 Certimate 的具体版本(请不要填写 `latest` 之类的无效版本号)。 / Please provide the specific version of Certimate.
|
||||||
|
placeholder: (e.g. v1.0.0)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 缺陷描述 / Description
|
||||||
|
description: 请详细清晰地描述你发现的缺陷或故障,如果可能请上传截图。 / Describe the bug you found in detail and clearly, and upload screenshots if possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 复现步骤 / Steps to reproduce
|
||||||
|
description: 请提供可复现的完整步骤。 / Please walk us through it step by step.
|
||||||
|
placeholder: |
|
||||||
|
1. ...
|
||||||
|
2. ...
|
||||||
|
3. ...
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 日志 / Logs
|
||||||
|
description: 在此处添加日志信息(如果有的话)。 / Add logs here if available.
|
||||||
|
value: |-
|
||||||
|
<details>
|
||||||
|
|
||||||
|
```console
|
||||||
|
# 请在此粘贴日志 / Paste logs here
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 其他 / Miscellaneous
|
||||||
|
description: 在此处添加关于该 Issue 的任何其他信息。 / Add any other context about the issue here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: 贡献 / Contribution
|
||||||
|
options:
|
||||||
|
- label: 我乐意为此贡献代码! / I am interested in contributing to this issue!
|
||||||
|
required: false
|
||||||
52
.github/ISSUE_TEMPLATE/2-feature_request.yml
vendored
Normal file
52
.github/ISSUE_TEMPLATE/2-feature_request.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: "💡 Feature Request"
|
||||||
|
description: "提出新功能请求或改进意见。 / Suggest an idea for this project."
|
||||||
|
title: "[Feature] 简要描述你希望实现的功能"
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Welcome!
|
||||||
|
|
||||||
|
**在提交 Issue 之前,请确认以下事项**:
|
||||||
|
1. 我**确认**是基于当前最新大版本而提出的新功能请求或改进意见。
|
||||||
|
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
|
||||||
|
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
|
||||||
|
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
|
||||||
|
5. 请保持每个 Issue 只包含一个功能请求。如果有多个需求,请分别提交 Issue。
|
||||||
|
|
||||||
|
**Before you submit the issue, please make sure of the following checklist**:
|
||||||
|
1. Yes, I'm using the latest release.
|
||||||
|
2. Yes, I've searched for [existing issues](https://github.com/usual2970/certimate/issues) (including closed ones) on GitHub and didn't find any similar.
|
||||||
|
3. Yes, I've read the [documentation](https://docs.certimate.me/en/) and didn't find any similar.
|
||||||
|
4. Please describe the problem in detail according to the template specification, otherwise the issue will be closed directly.
|
||||||
|
5. Please limit one request per issue.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 功能描述 / Description
|
||||||
|
description: 请详细清晰地描述你希望添加的功能,如果可能请上传截图。 / Describe the feature you'd like to add in detail and clearly, and upload screenshots if possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 请求动机 / Motivation
|
||||||
|
description: 为什么这个功能对项目有帮助? / Why is this feature helpful to the project?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 其他 / Miscellaneous
|
||||||
|
description: 在此处添加关于该 Issue 的任何其他信息(新增提供商请求请补充 API 文档链接等资料)。 / Add any other context about the problem here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: 贡献 / Contribution
|
||||||
|
options:
|
||||||
|
- label: 我乐意为此贡献代码! / I am interested in contributing to this issue!
|
||||||
|
required: false
|
||||||
44
.github/ISSUE_TEMPLATE/3-questions.yml
vendored
Normal file
44
.github/ISSUE_TEMPLATE/3-questions.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: "❓ Questions"
|
||||||
|
description: "遇到了困难需要求助? / Have problem in use and need help?"
|
||||||
|
title: "简要描述你遇到的问题"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Welcome!
|
||||||
|
|
||||||
|
**在提交 Issue 之前,请确认以下事项**:
|
||||||
|
1. 我**确认**正在使用的是当前最新版本。
|
||||||
|
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
|
||||||
|
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
|
||||||
|
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
|
||||||
|
5. 请保持每个 Issue 只包含一个问题求助。如果有多个问题,请分别提交 Issue。
|
||||||
|
|
||||||
|
**Before you submit the issue, please make sure of the following checklist**:
|
||||||
|
1. Yes, I'm using the latest release.
|
||||||
|
2. Yes, I've searched for [existing issues](https://github.com/usual2970/certimate/issues) (including closed ones) on GitHub and didn't find any similar.
|
||||||
|
3. Yes, I've read the [documentation](https://docs.certimate.me/en/) and didn't find any similar.
|
||||||
|
4. Please describe the problem in detail according to the template specification, otherwise the issue will be closed directly.
|
||||||
|
5. Please limit one question per issue.
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: 软件版本 / Release Version
|
||||||
|
description: 请提供 Certimate 的具体版本(请不要填写 `latest` 之类的无效版本号)。 / Please provide the specific version of Certimate.
|
||||||
|
placeholder: (e.g. v1.0.0)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 问题描述 / Description
|
||||||
|
description: 请详细清晰地描述你遇到的问题,如果可能请上传截图。 / Describe the problem you encountered in detail and clearly, and upload screenshots if possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 其他 / Miscellaneous
|
||||||
|
description: 在此处添加关于该问题的任何其他信息。 / Add any other context about the problem here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: 创建一个报告来帮助我们改进
|
|
||||||
title: "[Bug] 标题简要描述问题"
|
|
||||||
labels: bug
|
|
||||||
assignees: ""
|
|
||||||
---
|
|
||||||
|
|
||||||
**描述问题**
|
|
||||||
简要描述问题是什么,1 个 ISSUE 只描述一个问题。
|
|
||||||
|
|
||||||
**复现步骤**
|
|
||||||
复现该问题的步骤:
|
|
||||||
|
|
||||||
1. 去到 '...'
|
|
||||||
2. 点击 '...'
|
|
||||||
3. 滚动到 '...'
|
|
||||||
4. 发现问题
|
|
||||||
|
|
||||||
**期望的结果**
|
|
||||||
简要描述你期望发生的事情。
|
|
||||||
|
|
||||||
**截图**
|
|
||||||
如有可能,请添加截图以帮助解释问题。
|
|
||||||
|
|
||||||
**环境**
|
|
||||||
|
|
||||||
- 操作系统: [e.g. Windows, macOS]
|
|
||||||
- 浏览器: [e.g. Chrome, Safari]
|
|
||||||
- 仓库版本: [e.g. v1.0.0]
|
|
||||||
|
|
||||||
**其他信息**
|
|
||||||
在此处添加关于该问题的任何其他信息。
|
|
||||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: 加入频道讨论
|
- name: "🌐 加入频道讨论"
|
||||||
url: https://t.me/+ZXphsppxUg41YmVl
|
about: "加入到电报频道寻求更多帮助。 / Join in our Telegram channel."
|
||||||
about: 加入到电报频道寻求更多帮助
|
url: "https://t.me/+ZXphsppxUg41YmVl"
|
||||||
|
- name: "📖 常见问题"
|
||||||
|
about: "请先阅读文档 FAQ,可能会有你需要的答案。 / Please take a look to FAQs."
|
||||||
|
url: "https://docs.certimate.me/docs/reference/faq"
|
||||||
|
|||||||
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: 提出一个新功能请求
|
|
||||||
title: "[Feature] 简要描述你希望实现的功能"
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ""
|
|
||||||
---
|
|
||||||
|
|
||||||
**功能描述**
|
|
||||||
简要描述你希望添加的功能和相关问题,1 个 ISSUE 只描述一个功能。
|
|
||||||
|
|
||||||
**动机**
|
|
||||||
为什么这个功能对项目有帮助?
|
|
||||||
|
|
||||||
**替代方案**
|
|
||||||
描述你已经考虑过的替代方案。
|
|
||||||
|
|
||||||
**其他信息**
|
|
||||||
在这里添加任何相关的附加信息或截图。
|
|
||||||
6
.github/workflows/push_image.yml
vendored
6
.github/workflows/push_image.yml
vendored
@@ -34,6 +34,11 @@ jobs:
|
|||||||
images: |
|
images: |
|
||||||
usual2970/certimate
|
usual2970/certimate
|
||||||
registry.cn-shanghai.aliyuncs.com/usual2970/certimate
|
registry.cn-shanghai.aliyuncs.com/usual2970/certimate
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern=v{{version}}
|
||||||
|
type=semver,pattern=v{{major}}.{{minor}}
|
||||||
|
|
||||||
- name: Log in to DOCKERHUB
|
- name: Log in to DOCKERHUB
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -56,3 +61,4 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
|
||||||
|
|||||||
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Base Build
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -22,13 +22,22 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ">=1.23.0"
|
go-version-file: "go.mod"
|
||||||
|
|
||||||
- name: Build WebUI
|
- name: Build WebUI
|
||||||
run: npm --prefix=./ui ci && npm --prefix=./ui run build
|
run: |
|
||||||
|
npm --prefix=./ui ci
|
||||||
|
npm --prefix=./ui run build
|
||||||
|
npm cache clean --force
|
||||||
|
rm -rf ./ui/node_modules
|
||||||
|
|
||||||
|
- name: Check disk usage
|
||||||
|
run: |
|
||||||
|
df -h
|
||||||
|
du -sh /opt/hostedtoolcache/go/*
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v3
|
uses: goreleaser/goreleaser-action@v5
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: latest
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ release:
|
|||||||
archives:
|
archives:
|
||||||
- id: archive_noncgo
|
- id: archive_noncgo
|
||||||
builds: [build_noncgo]
|
builds: [build_noncgo]
|
||||||
format: zip
|
format: "zip"
|
||||||
files:
|
files:
|
||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- LICENSE.md
|
- LICENSE.md
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 前提条件
|
## 前提条件
|
||||||
|
|
||||||
- Go 1.22+ (用于修改 Go 代码)
|
- Go 1.24+ (用于修改 Go 代码)
|
||||||
- Node 20+ (用于修改 UI)
|
- Node 20+ (用于修改 UI)
|
||||||
|
|
||||||
如果还没有这样做,你可以 fork Certimate 的主仓库,并克隆到本地以便进行修改:
|
如果还没有这样做,你可以 fork Certimate 的主仓库,并克隆到本地以便进行修改:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Thank you for taking the time to improve Certimate! Below is a guide for submitt
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Go 1.22+ (for Go code changes)
|
- Go 1.24+ (for Go code changes)
|
||||||
- Node 20+ (for Admin UI changes)
|
- Node 20+ (for Admin UI changes)
|
||||||
|
|
||||||
If you haven't done so already, you can fork the Certimate repository and clone your fork to work locally:
|
If you haven't done so already, you can fork the Certimate repository and clone your fork to work locally:
|
||||||
|
|||||||
18
Dockerfile
18
Dockerfile
@@ -1,33 +1,23 @@
|
|||||||
FROM node:20-alpine3.19 AS front-builder
|
FROM node:20-alpine3.19 AS webui-builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY . /app/
|
COPY . /app/
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
cd /app/ui && \
|
cd /app/ui && \
|
||||||
npm install && \
|
npm install && \
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
|
|
||||||
FROM golang:1.23-alpine AS builder
|
|
||||||
|
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY ../. /app/
|
COPY ../. /app/
|
||||||
|
|
||||||
RUN rm -rf /app/ui/dist
|
RUN rm -rf /app/ui/dist
|
||||||
|
COPY --from=webui-builder /app/ui/dist /app/ui/dist
|
||||||
COPY --from=front-builder /app/ui/dist /app/ui/dist
|
|
||||||
|
|
||||||
RUN go build -o certimate
|
RUN go build -o certimate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=builder /app/certimate .
|
COPY --from=builder /app/certimate .
|
||||||
|
ENTRYPOINT ["./certimate", "serve", "--http", "0.0.0.0:8090"]
|
||||||
ENTRYPOINT ["./certimate", "serve", "--http", "0.0.0.0:8090"]
|
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
|||||||
- 灵活的工作流编排方式,证书从申请到部署完全自动化;
|
- 灵活的工作流编排方式,证书从申请到部署完全自动化;
|
||||||
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
|
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
|
||||||
- 支持 PEM、PFX、JKS 等多种格式输出证书;
|
- 支持 PEM、PFX、JKS 等多种格式输出证书;
|
||||||
- 支持 20+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看](https://docs.certimate.me/docs/reference/providers#supported-dns-providers)完整提供商清单);
|
- 支持 30+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers));
|
||||||
- 支持 60+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看](https://docs.certimate.me/docs/reference/providers#supported-host-providers)完整提供商清单);
|
- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-host-providers));
|
||||||
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
|
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
|
||||||
- 支持 Let's Encrypt、ZeroSSL、Google Trust Services 等多种 ACME 证书颁发机构;
|
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
|
||||||
- 更多特性等待探索。
|
- 更多特性等待探索。
|
||||||
|
|
||||||
## ⏱️ 快速启动
|
## ⏱️ 快速启动
|
||||||
@@ -71,6 +71,7 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
|||||||
|
|
||||||
相关文章:
|
相关文章:
|
||||||
|
|
||||||
|
- [使用 CNAME 完成 ACME DNS-01 质询](https://docs.certimate.me/blog/cname)
|
||||||
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
|
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
|
||||||
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
|
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
|
||||||
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate
|
|||||||
- Flexible workflow orchestration, fully automation from certificate application to deployment;
|
- Flexible workflow orchestration, fully automation from certificate application to deployment;
|
||||||
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
|
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
|
||||||
- Supports various certificate formats such as PEM, PFX, JKS.
|
- Supports various certificate formats such as PEM, PFX, JKS.
|
||||||
- Supports more than 20+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers));
|
- Supports more than 30+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers));
|
||||||
- Supports more than 60+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-host-providers));
|
- Supports more than 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-host-providers));
|
||||||
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
|
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
|
||||||
- Supports multiple ACME CAs including Let's Encrypt, ZeroSSL, Google Trust Services, and more;
|
- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust Services,SSL.com, ZeroSSL, and more;
|
||||||
- More features waiting to be discovered.
|
- More features waiting to be discovered.
|
||||||
|
|
||||||
## ⏱️ Fast Track
|
## ⏱️ Fast Track
|
||||||
@@ -69,6 +69,7 @@ Please visit the documentation site [docs.certimate.me](https://docs.certimate.m
|
|||||||
|
|
||||||
Related articles:
|
Related articles:
|
||||||
|
|
||||||
|
- [使用 CNAME 完成 ACME DNS-01 质询](https://docs.certimate.me/blog/cname)
|
||||||
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
|
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
|
||||||
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
|
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
|
||||||
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)
|
||||||
|
|||||||
@@ -6,5 +6,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8090:8090
|
- 8090:8090
|
||||||
volumes:
|
volumes:
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- ./data:/app/pb_data
|
- ./data:/app/pb_data
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
216
go.mod
216
go.mod
@@ -1,83 +1,93 @@
|
|||||||
module github.com/usual2970/certimate
|
module github.com/usual2970/certimate
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.23.2
|
toolchain go1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1
|
||||||
github.com/G-Core/gcorelabscdn-go v1.0.26
|
github.com/Edgio/edgio-api v0.0.0-workspace
|
||||||
|
github.com/G-Core/gcorelabscdn-go v1.0.31
|
||||||
github.com/alibabacloud-go/alb-20200616/v2 v2.2.8
|
github.com/alibabacloud-go/alb-20200616/v2 v2.2.8
|
||||||
|
github.com/alibabacloud-go/apig-20240327/v3 v3.2.2
|
||||||
github.com/alibabacloud-go/cas-20200407/v3 v3.0.4
|
github.com/alibabacloud-go/cas-20200407/v3 v3.0.4
|
||||||
github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2
|
github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.2
|
github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3
|
||||||
github.com/alibabacloud-go/esa-20240910/v2 v2.22.1
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7
|
||||||
|
github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0
|
||||||
|
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0
|
||||||
|
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5
|
||||||
|
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12
|
||||||
github.com/alibabacloud-go/live-20161101 v1.1.1
|
github.com/alibabacloud-go/live-20161101 v1.1.1
|
||||||
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
|
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
|
||||||
github.com/alibabacloud-go/slb-20140515/v4 v4.0.10
|
github.com/alibabacloud-go/slb-20140515/v4 v4.0.10
|
||||||
github.com/alibabacloud-go/tea v1.3.2
|
github.com/alibabacloud-go/tea v1.3.9
|
||||||
github.com/alibabacloud-go/vod-20170321/v4 v4.6.1
|
github.com/alibabacloud-go/vod-20170321/v4 v4.8.4
|
||||||
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5
|
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2
|
||||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||||
github.com/aws/aws-sdk-go-v2/service/acm v1.31.1
|
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0
|
||||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.1
|
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1
|
||||||
github.com/baidubce/bce-sdk-go v0.9.218
|
github.com/baidubce/bce-sdk-go v0.9.226
|
||||||
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.41
|
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46
|
||||||
github.com/go-acme/lego/v4 v4.22.2
|
github.com/go-acme/lego/v4 v4.23.1
|
||||||
github.com/go-resty/resty/v2 v2.16.5
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1
|
github.com/go-viper/mapstructure/v2 v2.2.1
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.138
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148
|
||||||
github.com/jdcloud-api/jdcloud-sdk-go v1.62.0
|
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
|
||||||
|
github.com/libdns/dynv6 v1.0.0
|
||||||
|
github.com/libdns/libdns v0.2.3
|
||||||
|
github.com/luthermonson/go-proxmox v0.2.2
|
||||||
github.com/nikoksr/notify v1.3.0
|
github.com/nikoksr/notify v1.3.0
|
||||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
|
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
|
||||||
github.com/pkg/sftp v1.13.7
|
github.com/pkg/sftp v1.13.9
|
||||||
github.com/pocketbase/dbx v1.11.0
|
github.com/pocketbase/dbx v1.11.0
|
||||||
github.com/pocketbase/pocketbase v0.25.9
|
github.com/pocketbase/pocketbase v0.28.0
|
||||||
github.com/povsister/scp v0.0.0-20240802064259-28781e87b246
|
github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c
|
||||||
github.com/qiniu/go-sdk/v7 v7.25.2
|
github.com/qiniu/go-sdk/v7 v7.25.3
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1115
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160
|
||||||
github.com/ucloud/ucloud-sdk-go v0.22.31
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162
|
||||||
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9
|
github.com/ucloud/ucloud-sdk-go v0.22.35
|
||||||
github.com/volcengine/volc-sdk-golang v1.0.197
|
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12
|
||||||
github.com/volcengine/volcengine-go-sdk v1.0.184
|
github.com/volcengine/volc-sdk-golang v1.0.207
|
||||||
|
github.com/volcengine/volcengine-go-sdk v1.1.7
|
||||||
gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1
|
gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1
|
||||||
gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0
|
gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.38.0
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||||
k8s.io/api v0.32.2
|
k8s.io/api v0.33.0
|
||||||
k8s.io/apimachinery v0.32.2
|
k8s.io/apimachinery v0.33.0
|
||||||
k8s.io/client-go v0.32.2
|
k8s.io/client-go v0.33.0
|
||||||
software.sslmate.com/src/go-pkcs12 v0.5.0
|
software.sslmate.com/src/go-pkcs12 v0.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
|
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-fc-util v0.0.7 // indirect
|
github.com/alibabacloud-go/alibabacloud-gateway-fc-util v0.0.7 // indirect
|
||||||
github.com/alibabacloud-go/fc-20230330/v4 v4.1.7 // indirect
|
|
||||||
github.com/alibabacloud-go/fc-open-20210406 v1.1.14 // indirect
|
|
||||||
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 // indirect
|
|
||||||
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect
|
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect
|
||||||
github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect
|
github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect
|
||||||
github.com/alibabacloud-go/tea-oss-sdk v1.1.3 // indirect
|
github.com/alibabacloud-go/tea-oss-sdk v1.1.5 // indirect
|
||||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
||||||
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
||||||
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
||||||
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
|
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
||||||
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/go-lark/lark v1.15.1 // indirect
|
github.com/go-lark/lark v1.15.1 // indirect
|
||||||
@@ -88,28 +98,35 @@ require (
|
|||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/gnostic-models v0.6.9 // indirect
|
github.com/google/gnostic-models v0.6.9 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
|
github.com/jinzhu/copier v0.3.4 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/magefile/mage v1.14.0 // indirect
|
||||||
github.com/mailru/easyjson v0.9.0 // indirect
|
github.com/mailru/easyjson v0.9.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||||
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||||
|
github.com/nrdcg/desec v0.10.0 // indirect
|
||||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||||
|
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||||
|
github.com/peterhellberg/link v1.2.0 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/qiniu/dyn v1.3.0 // indirect
|
github.com/qiniu/dyn v1.3.0 // indirect
|
||||||
github.com/qiniu/x v1.10.5 // indirect
|
github.com/qiniu/x v1.10.5 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115 // indirect
|
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
@@ -117,15 +134,16 @@ require (
|
|||||||
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
||||||
github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0
|
github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0
|
||||||
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
github.com/alibabacloud-go/debug v1.0.1 // indirect
|
||||||
@@ -133,91 +151,79 @@ require (
|
|||||||
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
|
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
|
||||||
github.com/aliyun/credentials-go v1.4.3 // indirect
|
github.com/aliyun/credentials-go v1.4.6 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.36.3
|
github.com/aws/aws-sdk-go-v2 v1.36.3
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.29.9
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.29.5
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.62
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.58
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
|
|
||||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.58 // indirect
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.75.3 // indirect
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
|
|
||||||
github.com/aws/smithy-go v1.22.2 // indirect
|
github.com/aws/smithy-go v1.22.2 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.114.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/disintegration/imaging v1.6.2 // indirect
|
github.com/disintegration/imaging v1.6.2 // indirect
|
||||||
github.com/domodwyer/mailyak/v3 v3.6.2
|
github.com/domodwyer/mailyak/v3 v3.6.2
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
|
github.com/ganigeorgiev/fexpr v0.5.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.4 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/miekg/dns v1.1.62 // indirect
|
github.com/miekg/dns v1.1.64 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.8.0 // indirect
|
||||||
github.com/spf13/cobra v1.8.1 // indirect
|
github.com/spf13/cobra v1.9.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
golang.org/x/image v0.27.0 // indirect
|
||||||
gocloud.dev v0.40.0 // indirect
|
|
||||||
golang.org/x/image v0.24.0 // indirect
|
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/net v0.37.0 // indirect
|
golang.org/x/net v0.40.0 // indirect
|
||||||
golang.org/x/oauth2 v0.26.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.12.0
|
golang.org/x/sync v0.14.0
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/term v0.30.0 // indirect
|
golang.org/x/term v0.32.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/time v0.9.0
|
golang.org/x/time v0.11.0
|
||||||
golang.org/x/tools v0.31.0 // indirect
|
golang.org/x/tools v0.33.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
|
||||||
google.golang.org/api v0.220.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
|
|
||||||
google.golang.org/grpc v1.70.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.61.13 // indirect
|
modernc.org/libc v1.62.1 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.8.2 // indirect
|
modernc.org/memory v1.9.1 // indirect
|
||||||
modernc.org/sqlite v1.35.0 // indirect
|
modernc.org/sqlite v1.37.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0
|
replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace
|
||||||
|
|
||||||
replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1
|
replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 => ./internal/pkg/sdk3rd/cmcc/ecloudsdkcore@v1.0.0
|
||||||
|
|
||||||
|
replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
_ "time/tzdata"
|
||||||
|
|
||||||
"github.com/pocketbase/pocketbase/tools/cron"
|
"github.com/pocketbase/pocketbase/tools/cron"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,38 +1,30 @@
|
|||||||
package applicant
|
package applicant
|
||||||
|
|
||||||
|
import "github.com/usual2970/certimate/internal/domain"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sslProviderLetsEncrypt = "letsencrypt"
|
sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
|
||||||
sslProviderLetsEncryptStaging = "letsencrypt_staging"
|
sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
|
||||||
sslProviderZeroSSL = "zerossl"
|
sslProviderBuypass = string(domain.CAProviderTypeBuypass)
|
||||||
sslProviderGoogleTrustServices = "gts"
|
sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
|
||||||
)
|
sslProviderSSLCom = string(domain.CAProviderTypeSSLCom)
|
||||||
const defaultSSLProvider = sslProviderLetsEncrypt
|
sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL)
|
||||||
|
|
||||||
const (
|
sslProviderDefault = sslProviderLetsEncrypt
|
||||||
letsencryptUrl = "https://acme-v02.api.letsencrypt.org/directory"
|
|
||||||
letsencryptStagingUrl = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
|
||||||
zerosslUrl = "https://acme.zerossl.com/v2/DV90"
|
|
||||||
gtsUrl = "https://dv.acme-v02.api.pki.goog/directory"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var sslProviderUrls = map[string]string{
|
var sslProviderUrls = map[string]string{
|
||||||
sslProviderLetsEncrypt: letsencryptUrl,
|
sslProviderLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
sslProviderLetsEncryptStaging: letsencryptStagingUrl,
|
sslProviderLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||||
sslProviderZeroSSL: zerosslUrl,
|
sslProviderBuypass: "https://api.buypass.com/acme/directory",
|
||||||
sslProviderGoogleTrustServices: gtsUrl,
|
sslProviderGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory",
|
||||||
|
sslProviderSSLCom: "https://acme.ssl.com/sslcom-dv-rsa",
|
||||||
|
sslProviderSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa",
|
||||||
|
sslProviderSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc",
|
||||||
|
sslProviderZeroSSL: "https://acme.zerossl.com/v2/DV90",
|
||||||
}
|
}
|
||||||
|
|
||||||
type acmeSSLProviderConfig struct {
|
type acmeSSLProviderConfig struct {
|
||||||
Config acmeSSLProviderConfigContent `json:"config"`
|
Config map[domain.CAProviderType]map[string]any `json:"config"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
}
|
|
||||||
|
|
||||||
type acmeSSLProviderConfigContent struct {
|
|
||||||
ZeroSSL acmeSSLProviderEabConfig `json:"zerossl"`
|
|
||||||
GoogleTrustServices acmeSSLProviderEabConfig `json:"gts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type acmeSSLProviderEabConfig struct {
|
|
||||||
EabHmacKey string `json:"eabHmacKey"`
|
|
||||||
EabKid string `json:"eabKid"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package applicant
|
package applicant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -13,7 +13,8 @@ import (
|
|||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ func newAcmeUser(ca, email string) (*acmeUser, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPEM, err := certs.ConvertECPrivateKeyToPEM(key)
|
keyPEM, err := certutil.ConvertECPrivateKeyToPEM(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ func (u acmeUser) GetRegistration() *registration.Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *acmeUser) GetPrivateKey() crypto.PrivateKey {
|
func (u *acmeUser) GetPrivateKey() crypto.PrivateKey {
|
||||||
rs, _ := certs.ParseECPrivateKeyFromPEM(u.privkey)
|
rs, _ := certutil.ParseECPrivateKeyFromPEM(u.privkey)
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,16 +77,11 @@ func (u *acmeUser) getPrivateKeyPEM() string {
|
|||||||
return u.privkey
|
return u.privkey
|
||||||
}
|
}
|
||||||
|
|
||||||
type acmeAccountRepository interface {
|
|
||||||
GetByCAAndEmail(ca, email string) (*domain.AcmeAccount, error)
|
|
||||||
Save(ca, email, key string, resource *registration.Resource) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var registerGroup singleflight.Group
|
var registerGroup singleflight.Group
|
||||||
|
|
||||||
func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
|
func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
|
||||||
resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", sslProviderConfig.Provider, user.GetEmail()), func() (interface{}, error) {
|
resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", user.CA, user.Email), func() (interface{}, error) {
|
||||||
return registerAcmeUser(client, sslProviderConfig, user)
|
return registerAcmeUser(client, user, userRegisterOptions)
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -95,45 +91,81 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *ac
|
|||||||
return resp.(*registration.Resource), nil
|
return resp.(*registration.Resource), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerAcmeUser(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
|
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
|
||||||
var reg *registration.Resource
|
var reg *registration.Resource
|
||||||
var err error
|
var err error
|
||||||
switch sslProviderConfig.Provider {
|
switch user.CA {
|
||||||
case sslProviderZeroSSL:
|
|
||||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
|
||||||
TermsOfServiceAgreed: true,
|
|
||||||
Kid: sslProviderConfig.Config.ZeroSSL.EabKid,
|
|
||||||
HmacEncoded: sslProviderConfig.Config.ZeroSSL.EabHmacKey,
|
|
||||||
})
|
|
||||||
case sslProviderGoogleTrustServices:
|
|
||||||
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
|
||||||
TermsOfServiceAgreed: true,
|
|
||||||
Kid: sslProviderConfig.Config.GoogleTrustServices.EabKid,
|
|
||||||
HmacEncoded: sslProviderConfig.Config.GoogleTrustServices.EabHmacKey,
|
|
||||||
})
|
|
||||||
case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
|
case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
|
||||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
|
||||||
|
case sslProviderBuypass:
|
||||||
|
{
|
||||||
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
case sslProviderGoogleTrustServices:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForGoogleTrustServices{}
|
||||||
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: access.EabKid,
|
||||||
|
HmacEncoded: access.EabHmacKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case sslProviderSSLCom:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForSSLCom{}
|
||||||
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: access.EabKid,
|
||||||
|
HmacEncoded: access.EabHmacKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case sslProviderZeroSSL:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForZeroSSL{}
|
||||||
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: access.EabKid,
|
||||||
|
HmacEncoded: access.EabHmacKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unsupported ssl provider: %s", sslProviderConfig.Provider)
|
err = fmt.Errorf("unsupported ca provider '%s'", user.CA)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := repository.NewAcmeAccountRepository()
|
repo := repository.NewAcmeAccountRepository()
|
||||||
resp, err := repo.GetByCAAndEmail(sslProviderConfig.Provider, user.GetEmail())
|
resp, err := repo.GetByCAAndEmail(user.CA, user.Email)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
user.privkey = resp.Key
|
user.privkey = resp.Key
|
||||||
return resp.Resource, nil
|
return resp.Resource, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := repo.Save(context.Background(), &domain.AcmeAccount{
|
if _, err := repo.Save(context.Background(), &domain.AcmeAccount{
|
||||||
CA: sslProviderConfig.Provider,
|
CA: user.CA,
|
||||||
Email: user.GetEmail(),
|
Email: user.Email,
|
||||||
Key: user.getPrivateKeyPEM(),
|
Key: user.getPrivateKeyPEM(),
|
||||||
Resource: reg,
|
Resource: reg,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, fmt.Errorf("failed to save registration: %w", err)
|
return nil, fmt.Errorf("failed to save acme account registration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return reg, nil
|
return reg, nil
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
@@ -18,11 +20,11 @@ import (
|
|||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
uslices "github.com/usual2970/certimate/internal/pkg/utils/slices"
|
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyCertResult struct {
|
type ApplyResult struct {
|
||||||
CertificateFullChain string
|
CertificateFullChain string
|
||||||
IssuerCertificate string
|
IssuerCertificate string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
@@ -33,56 +35,78 @@ type ApplyCertResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Applicant interface {
|
type Applicant interface {
|
||||||
Apply() (*ApplyCertResult, error)
|
Apply(ctx context.Context) (*ApplyResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type applicantOptions struct {
|
type ApplicantWithWorkflowNodeConfig struct {
|
||||||
Domains []string
|
Node *domain.WorkflowNode
|
||||||
ContactEmail string
|
Logger *slog.Logger
|
||||||
Provider domain.ApplyDNSProviderType
|
|
||||||
ProviderAccessConfig map[string]any
|
|
||||||
ProviderApplyConfig map[string]any
|
|
||||||
KeyAlgorithm string
|
|
||||||
Nameservers []string
|
|
||||||
DnsPropagationTimeout int32
|
|
||||||
DnsTTL int32
|
|
||||||
DisableFollowCNAME bool
|
|
||||||
ReplacedARIAcctId string
|
|
||||||
ReplacedARICertId string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, error) {
|
||||||
if node.Type != domain.WorkflowNodeTypeApply {
|
if config.Node == nil {
|
||||||
return nil, fmt.Errorf("node type is not apply")
|
return nil, fmt.Errorf("node is nil")
|
||||||
|
}
|
||||||
|
if config.Node.Type != domain.WorkflowNodeTypeApply {
|
||||||
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply))
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig := node.GetConfigForApply()
|
nodeConfig := config.Node.GetConfigForApply()
|
||||||
options := &applicantOptions{
|
options := &applicantProviderOptions{
|
||||||
Domains: uslices.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
||||||
ContactEmail: nodeConfig.ContactEmail,
|
ContactEmail: nodeConfig.ContactEmail,
|
||||||
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
|
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
|
||||||
ProviderApplyConfig: nodeConfig.ProviderConfig,
|
ProviderAccessConfig: make(map[string]any),
|
||||||
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
Nameservers: uslices.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
|
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
|
||||||
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
|
CAProviderAccessConfig: make(map[string]any),
|
||||||
DnsTTL: nodeConfig.DnsTTL,
|
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
|
||||||
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
|
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
||||||
|
Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
|
||||||
|
DnsPropagationWait: nodeConfig.DnsPropagationWait,
|
||||||
|
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
|
||||||
|
DnsTTL: nodeConfig.DnsTTL,
|
||||||
|
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
|
||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil {
|
if nodeConfig.ProviderAccessId != "" {
|
||||||
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil {
|
||||||
} else {
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
||||||
accessConfig, err := access.UnmarshalConfigToMap()
|
} else {
|
||||||
if err != nil {
|
options.ProviderAccessConfig = access.Config
|
||||||
return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
|
}
|
||||||
|
}
|
||||||
|
if nodeConfig.CAProviderAccessId != "" {
|
||||||
|
if access, err := accessRepo.GetById(context.Background(), nodeConfig.CAProviderAccessId); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.CAProviderAccessId, err)
|
||||||
|
} else {
|
||||||
|
options.CAProviderAccessConfig = access.Config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsRepo := repository.NewSettingsRepository()
|
||||||
|
if string(options.CAProvider) == "" {
|
||||||
|
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
|
||||||
|
|
||||||
|
sslProviderConfig := &acmeSSLProviderConfig{
|
||||||
|
Config: make(map[domain.CAProviderType]map[string]any),
|
||||||
|
Provider: sslProviderDefault,
|
||||||
|
}
|
||||||
|
if settings != nil {
|
||||||
|
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if sslProviderConfig.Provider == "" {
|
||||||
|
sslProviderConfig.Provider = sslProviderDefault
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.ProviderAccessConfig = accessConfig
|
options.CAProvider = domain.CAProviderType(sslProviderConfig.Provider)
|
||||||
|
options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider]
|
||||||
}
|
}
|
||||||
|
|
||||||
certRepo := repository.NewCertificateRepository()
|
certRepo := repository.NewCertificateRepository()
|
||||||
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), node.Id)
|
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id)
|
||||||
if lastCertificate != nil {
|
if lastCertificate != nil {
|
||||||
newCertSan := slices.Clone(options.Domains)
|
newCertSan := slices.Clone(options.Domains)
|
||||||
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
|
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
|
||||||
@@ -93,42 +117,53 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
|||||||
lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate))
|
lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate))
|
||||||
if lastCertX509 != nil {
|
if lastCertX509 != nil {
|
||||||
replacedARICertId, _ := certificate.MakeARICertID(lastCertX509)
|
replacedARICertId, _ := certificate.MakeARICertID(lastCertX509)
|
||||||
options.ReplacedARIAcctId = lastCertificate.ACMEAccountUrl
|
options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl
|
||||||
options.ReplacedARICertId = replacedARICertId
|
options.ReplacedARICert = replacedARICertId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := createApplicant(options)
|
applicant, err := createApplicantProvider(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyApplicant{
|
return &applicantImpl{
|
||||||
applicant: applicant,
|
applicant: applicant,
|
||||||
options: options,
|
options: options,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) {
|
type applicantImpl struct {
|
||||||
settingsRepo := repository.NewSettingsRepository()
|
applicant challenge.Provider
|
||||||
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
|
options *applicantProviderOptions
|
||||||
|
}
|
||||||
|
|
||||||
sslProviderConfig := &acmeSSLProviderConfig{
|
var _ Applicant = (*applicantImpl)(nil)
|
||||||
Config: acmeSSLProviderConfigContent{},
|
|
||||||
Provider: defaultSSLProvider,
|
func (d *applicantImpl) Apply(ctx context.Context) (*ApplyResult, error) {
|
||||||
}
|
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
|
||||||
if settings != nil {
|
if err := limiter.Wait(ctx); err != nil {
|
||||||
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sslProviderConfig.Provider == "" {
|
return applyUseLego(d.applicant, d.options)
|
||||||
sslProviderConfig.Provider = defaultSSLProvider
|
}
|
||||||
}
|
|
||||||
|
|
||||||
acmeUser, err := newAcmeUser(sslProviderConfig.Provider, options.ContactEmail)
|
const (
|
||||||
|
limitBurst = 300
|
||||||
|
limitRate float64 = float64(1) / float64(36)
|
||||||
|
)
|
||||||
|
|
||||||
|
var limiters sync.Map
|
||||||
|
|
||||||
|
func getLimiter(key string) *rate.Limiter {
|
||||||
|
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
|
||||||
|
return limiter.(*rate.Limiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
|
||||||
|
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -138,9 +173,16 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(options.DisableFollowCNAME))
|
os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(options.DisableFollowCNAME))
|
||||||
|
|
||||||
// Create an ACME client config
|
// Create an ACME client config
|
||||||
config := lego.NewConfig(acmeUser)
|
config := lego.NewConfig(user)
|
||||||
config.CADirURL = sslProviderUrls[sslProviderConfig.Provider]
|
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
||||||
config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
config.CADirURL = sslProviderUrls[user.CA]
|
||||||
|
if user.CA == sslProviderSSLCom {
|
||||||
|
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
|
||||||
|
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"]
|
||||||
|
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
|
||||||
|
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create an ACME client
|
// Create an ACME client
|
||||||
client, err := lego.NewClient(config)
|
client, err := lego.NewClient(config)
|
||||||
@@ -149,20 +191,28 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the DNS01 challenge provider
|
// Set the DNS01 challenge provider
|
||||||
challengeOptions := make([]dns01.ChallengeOption, 0)
|
client.Challenge.SetDNS01Provider(legoProvider,
|
||||||
if len(options.Nameservers) > 0 {
|
dns01.CondOption(
|
||||||
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
|
len(options.Nameservers) > 0,
|
||||||
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
|
dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)),
|
||||||
}
|
),
|
||||||
client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...)
|
dns01.CondOption(
|
||||||
|
options.DnsPropagationWait > 0,
|
||||||
|
dns01.PropagationWait(time.Duration(options.DnsPropagationWait)*time.Second, true),
|
||||||
|
),
|
||||||
|
dns01.CondOption(
|
||||||
|
len(options.Nameservers) > 0 || options.DnsPropagationWait > 0,
|
||||||
|
dns01.DisableAuthoritativeNssPropagationRequirement(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// New users need to register first
|
// New users need to register first
|
||||||
if !acmeUser.hasRegistration() {
|
if !user.hasRegistration() {
|
||||||
reg, err := registerAcmeUserWithSingleFlight(client, sslProviderConfig, acmeUser)
|
reg, err := registerAcmeUserWithSingleFlight(client, user, options.CAProviderAccessConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to register: %w", err)
|
return nil, fmt.Errorf("failed to register acme user: %w", err)
|
||||||
}
|
}
|
||||||
acmeUser.Registration = reg
|
user.Registration = reg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain a certificate
|
// Obtain a certificate
|
||||||
@@ -170,64 +220,39 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
|
|||||||
Domains: options.Domains,
|
Domains: options.Domains,
|
||||||
Bundle: true,
|
Bundle: true,
|
||||||
}
|
}
|
||||||
if options.ReplacedARICertId != "" && options.ReplacedARIAcctId != acmeUser.Registration.URI {
|
if options.ReplacedARIAcct == user.Registration.URI {
|
||||||
certRequest.ReplacesCertID = options.ReplacedARICertId
|
certRequest.ReplacesCertID = options.ReplacedARICert
|
||||||
}
|
}
|
||||||
certResource, err := client.Certificate.Obtain(certRequest)
|
certResource, err := client.Certificate.Obtain(certRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ApplyCertResult{
|
return &ApplyResult{
|
||||||
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
|
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
|
||||||
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
||||||
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
||||||
ACMEAccountUrl: acmeUser.Registration.URI,
|
ACMEAccountUrl: user.Registration.URI,
|
||||||
ACMECertUrl: certResource.CertURL,
|
ACMECertUrl: certResource.CertURL,
|
||||||
ACMECertStableUrl: certResource.CertStableURL,
|
ACMECertStableUrl: certResource.CertStableURL,
|
||||||
CSR: strings.TrimSpace(string(certResource.CSR)),
|
CSR: strings.TrimSpace(string(certResource.CSR)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
|
func parseLegoKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
|
||||||
switch algo {
|
alogMap := map[domain.CertificateKeyAlgorithmType]certcrypto.KeyType{
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA2048:
|
domain.CertificateKeyAlgorithmTypeRSA2048: certcrypto.RSA2048,
|
||||||
return certcrypto.RSA2048
|
domain.CertificateKeyAlgorithmTypeRSA3072: certcrypto.RSA3072,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA3072:
|
domain.CertificateKeyAlgorithmTypeRSA4096: certcrypto.RSA4096,
|
||||||
return certcrypto.RSA3072
|
domain.CertificateKeyAlgorithmTypeRSA8192: certcrypto.RSA8192,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA4096:
|
domain.CertificateKeyAlgorithmTypeEC256: certcrypto.EC256,
|
||||||
return certcrypto.RSA4096
|
domain.CertificateKeyAlgorithmTypeEC384: certcrypto.EC384,
|
||||||
case domain.CertificateKeyAlgorithmTypeRSA8192:
|
domain.CertificateKeyAlgorithmTypeEC512: certcrypto.KeyType("P512"),
|
||||||
return certcrypto.RSA8192
|
}
|
||||||
case domain.CertificateKeyAlgorithmTypeEC256:
|
|
||||||
return certcrypto.EC256
|
if keyType, ok := alogMap[algo]; ok {
|
||||||
case domain.CertificateKeyAlgorithmTypeEC384:
|
return keyType
|
||||||
return certcrypto.EC384
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return certcrypto.RSA2048
|
return certcrypto.RSA2048
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
|
|
||||||
type proxyApplicant struct {
|
|
||||||
applicant challenge.Provider
|
|
||||||
options *applicantOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
var limiters sync.Map
|
|
||||||
|
|
||||||
const (
|
|
||||||
limitBurst = 300
|
|
||||||
limitRate float64 = float64(1) / float64(36)
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLimiter(key string) *rate.Limiter {
|
|
||||||
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
|
|
||||||
return limiter.(*rate.Limiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *proxyApplicant) Apply() (*ApplyCertResult, error) {
|
|
||||||
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
|
|
||||||
limiter.Wait(context.Background())
|
|
||||||
return apply(d.applicant, d.options)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,13 +8,17 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
pACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq"
|
pACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/acmehttpreq"
|
||||||
pAliyun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun"
|
pAliyun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun"
|
||||||
|
pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa"
|
||||||
pAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53"
|
pAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aws-route53"
|
||||||
pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns"
|
pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/azure-dns"
|
||||||
pBaiduCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud"
|
pBaiduCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud"
|
||||||
|
pBunny "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/bunny"
|
||||||
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
|
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudflare"
|
||||||
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
|
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns"
|
||||||
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
|
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud"
|
||||||
|
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
|
||||||
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
|
||||||
|
pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6"
|
||||||
pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore"
|
pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore"
|
||||||
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
|
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
|
||||||
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
|
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
|
||||||
@@ -23,25 +27,49 @@ import (
|
|||||||
pNamecheap "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap"
|
pNamecheap "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap"
|
||||||
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
|
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
|
||||||
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
|
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
|
||||||
|
pNetcup "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/netcup"
|
||||||
|
pNetlify "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/netlify"
|
||||||
pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
|
pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
|
||||||
|
pPorkbun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/porkbun"
|
||||||
pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns"
|
pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns"
|
||||||
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
|
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
|
||||||
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
|
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
|
||||||
|
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo"
|
||||||
|
pVercel "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/vercel"
|
||||||
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
|
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
|
||||||
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
|
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/maps"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
type applicantProviderOptions struct {
|
||||||
|
Domains []string
|
||||||
|
ContactEmail string
|
||||||
|
Provider domain.ACMEDns01ProviderType
|
||||||
|
ProviderAccessConfig map[string]any
|
||||||
|
ProviderExtendedConfig map[string]any
|
||||||
|
CAProvider domain.CAProviderType
|
||||||
|
CAProviderAccessConfig map[string]any
|
||||||
|
CAProviderExtendedConfig map[string]any
|
||||||
|
KeyAlgorithm string
|
||||||
|
Nameservers []string
|
||||||
|
DnsPropagationWait int32
|
||||||
|
DnsPropagationTimeout int32
|
||||||
|
DnsTTL int32
|
||||||
|
DisableFollowCNAME bool
|
||||||
|
ReplacedARIAcct string
|
||||||
|
ReplacedARICert string
|
||||||
|
}
|
||||||
|
|
||||||
|
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
|
||||||
/*
|
/*
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch options.Provider {
|
switch options.Provider {
|
||||||
case domain.ApplyDNSProviderTypeACMEHttpReq:
|
case domain.ACMEDns01ProviderTypeACMEHttpReq:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForACMEHttpReq{}
|
access := domain.AccessConfigForACMEHttpReq{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,44 +83,60 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS:
|
case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS, domain.ACMEDns01ProviderTypeAliyunESA:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAliyun{}
|
access := domain.AccessConfigForAliyun{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{
|
switch options.Provider {
|
||||||
AccessKeyId: access.AccessKeyId,
|
case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS:
|
||||||
AccessKeySecret: access.AccessKeySecret,
|
applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
AccessKeyId: access.AccessKeyId,
|
||||||
DnsTTL: options.DnsTTL,
|
AccessKeySecret: access.AccessKeySecret,
|
||||||
})
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
return applicant, err
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeAliyunESA:
|
||||||
|
applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{
|
||||||
|
AccessKeyId: access.AccessKeyId,
|
||||||
|
AccessKeySecret: access.AccessKeySecret,
|
||||||
|
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53:
|
case domain.ACMEDns01ProviderTypeAWS, domain.ACMEDns01ProviderTypeAWSRoute53:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAWS{}
|
access := domain.AccessConfigForAWS{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
|
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
SecretAccessKey: access.SecretAccessKey,
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
|
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
||||||
HostedZoneId: maps.GetValueAsString(options.ProviderApplyConfig, "hostedZoneId"),
|
HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
|
case domain.ACMEDns01ProviderTypeAzure, domain.ACMEDns01ProviderTypeAzureDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForAzure{}
|
access := domain.AccessConfigForAzure{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,10 +151,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS:
|
case domain.ACMEDns01ProviderTypeBaiduCloud, domain.ACMEDns01ProviderTypeBaiduCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForBaiduCloud{}
|
access := domain.AccessConfigForBaiduCloud{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,25 +167,41 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeCloudflare:
|
case domain.ACMEDns01ProviderTypeBunny:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForCloudflare{}
|
access := domain.AccessConfigForBunny{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pCloudflare.NewChallengeProvider(&pCloudflare.ChallengeProviderConfig{
|
applicant, err := pBunny.NewChallengeProvider(&pBunny.ChallengeProviderConfig{
|
||||||
DnsApiToken: access.DnsApiToken,
|
ApiKey: access.ApiKey,
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeClouDNS:
|
case domain.ACMEDns01ProviderTypeCloudflare:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForCloudflare{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pCloudflare.NewChallengeProvider(&pCloudflare.ChallengeProviderConfig{
|
||||||
|
DnsApiToken: access.DnsApiToken,
|
||||||
|
ZoneApiToken: access.ZoneApiToken,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeClouDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForClouDNS{}
|
access := domain.AccessConfigForClouDNS{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,10 +214,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeCMCCCloud:
|
case domain.ACMEDns01ProviderTypeCMCCCloud:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForCMCCCloud{}
|
access := domain.AccessConfigForCMCCCloud{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,10 +230,25 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeDNSLA:
|
case domain.ACMEDns01ProviderTypeDeSEC:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForDeSEC{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pDeSEC.NewChallengeProvider(&pDeSEC.ChallengeProviderConfig{
|
||||||
|
Token: access.Token,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeDNSLA:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForDNSLA{}
|
access := domain.AccessConfigForDNSLA{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,10 +261,25 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGcore:
|
case domain.ACMEDns01ProviderTypeDynv6:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForDynv6{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pDynv6.NewChallengeProvider(&pDynv6.ChallengeProviderConfig{
|
||||||
|
HttpToken: access.HttpToken,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeGcore:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGcore{}
|
access := domain.AccessConfigForGcore{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,10 +291,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGname:
|
case domain.ACMEDns01ProviderTypeGname:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGname{}
|
access := domain.AccessConfigForGname{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,10 +307,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeGoDaddy:
|
case domain.ACMEDns01ProviderTypeGoDaddy:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGoDaddy{}
|
access := domain.AccessConfigForGoDaddy{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,44 +323,44 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS:
|
case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForHuaweiCloud{}
|
access := domain.AccessConfigForHuaweiCloud{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
|
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
SecretAccessKey: access.SecretAccessKey,
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
|
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
|
case domain.ACMEDns01ProviderTypeJDCloud, domain.ACMEDns01ProviderTypeJDCloudDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForJDCloud{}
|
access := domain.AccessConfigForJDCloud{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
|
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
AccessKeySecret: access.AccessKeySecret,
|
AccessKeySecret: access.AccessKeySecret,
|
||||||
RegionId: maps.GetValueAsString(options.ProviderApplyConfig, "region_id"),
|
RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNamecheap:
|
case domain.ACMEDns01ProviderTypeNamecheap:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNamecheap{}
|
access := domain.AccessConfigForNamecheap{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,10 +373,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNameDotCom:
|
case domain.ACMEDns01ProviderTypeNameDotCom:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNameDotCom{}
|
access := domain.AccessConfigForNameDotCom{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,10 +389,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNameSilo:
|
case domain.ACMEDns01ProviderTypeNameSilo:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNameSilo{}
|
access := domain.AccessConfigForNameSilo{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,10 +404,42 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeNS1:
|
case domain.ACMEDns01ProviderTypeNetcup:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForNetcup{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pNetcup.NewChallengeProvider(&pNetcup.ChallengeProviderConfig{
|
||||||
|
CustomerNumber: access.CustomerNumber,
|
||||||
|
ApiKey: access.ApiKey,
|
||||||
|
ApiPassword: access.ApiPassword,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeNetlify:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForNetlify{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pNetlify.NewChallengeProvider(&pNetlify.ChallengeProviderConfig{
|
||||||
|
ApiToken: access.ApiToken,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeNS1:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForNS1{}
|
access := domain.AccessConfigForNS1{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,26 +451,43 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypePowerDNS:
|
case domain.ACMEDns01ProviderTypePorkbun:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForPowerDNS{}
|
access := domain.AccessConfigForPorkbun{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{
|
applicant, err := pPorkbun.NewChallengeProvider(&pPorkbun.ChallengeProviderConfig{
|
||||||
ApiUrl: access.ApiUrl,
|
|
||||||
ApiKey: access.ApiKey,
|
ApiKey: access.ApiKey,
|
||||||
|
SecretApiKey: access.SecretApiKey,
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeRainYun:
|
case domain.ACMEDns01ProviderTypePowerDNS:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForPowerDNS{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{
|
||||||
|
ApiUrl: access.ApiUrl,
|
||||||
|
ApiKey: access.ApiKey,
|
||||||
|
AllowInsecureConnections: access.AllowInsecureConnections,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeRainYun:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForRainYun{}
|
access := domain.AccessConfigForRainYun{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,26 +499,58 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
|
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS, domain.ACMEDns01ProviderTypeTencentCloudEO:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForTencentCloud{}
|
access := domain.AccessConfigForTencentCloud{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
|
switch options.Provider {
|
||||||
SecretId: access.SecretId,
|
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS:
|
||||||
SecretKey: access.SecretKey,
|
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
|
||||||
|
SecretId: access.SecretId,
|
||||||
|
SecretKey: access.SecretKey,
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeTencentCloudEO:
|
||||||
|
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
|
||||||
|
SecretId: access.SecretId,
|
||||||
|
SecretKey: access.SecretKey,
|
||||||
|
ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
|
||||||
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
|
DnsTTL: options.DnsTTL,
|
||||||
|
})
|
||||||
|
return applicant, err
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.ACMEDns01ProviderTypeVercel:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForVercel{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applicant, err := pVercel.NewChallengeProvider(&pVercel.ChallengeProviderConfig{
|
||||||
|
ApiAccessToken: access.ApiAccessToken,
|
||||||
|
TeamId: access.TeamId,
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS:
|
case domain.ACMEDns01ProviderTypeVolcEngine, domain.ACMEDns01ProviderTypeVolcEngineDNS:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForVolcEngine{}
|
access := domain.AccessConfigForVolcEngine{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,10 +563,10 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
return applicant, err
|
return applicant, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.ApplyDNSProviderTypeWestcn:
|
case domain.ACMEDns01ProviderTypeWestcn:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForWestcn{}
|
access := domain.AccessConfigForWestcn{}
|
||||||
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,5 +580,5 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unsupported applicant provider: %s", string(options.Provider))
|
return nil, fmt.Errorf("unsupported applicant provider '%s'", string(options.Provider))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/app"
|
"github.com/usual2970/certimate/internal/app"
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/domain/dtos"
|
"github.com/usual2970/certimate/internal/domain/dtos"
|
||||||
"github.com/usual2970/certimate/internal/notify"
|
"github.com/usual2970/certimate/internal/notify"
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,21 +29,29 @@ const (
|
|||||||
type certificateRepository interface {
|
type certificateRepository interface {
|
||||||
ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error)
|
ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error)
|
||||||
GetById(ctx context.Context, id string) (*domain.Certificate, error)
|
GetById(ctx context.Context, id string) (*domain.Certificate, error)
|
||||||
|
DeleteWhere(ctx context.Context, exprs ...dbx.Expression) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type settingsRepository interface {
|
||||||
|
GetByName(ctx context.Context, name string) (*domain.Settings, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CertificateService struct {
|
type CertificateService struct {
|
||||||
certRepo certificateRepository
|
certificateRepo certificateRepository
|
||||||
|
settingsRepo settingsRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCertificateService(certRepo certificateRepository) *CertificateService {
|
func NewCertificateService(certificateRepo certificateRepository, settingsRepo settingsRepository) *CertificateService {
|
||||||
return &CertificateService{
|
return &CertificateService{
|
||||||
certRepo: certRepo,
|
certificateRepo: certificateRepo,
|
||||||
|
settingsRepo: settingsRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CertificateService) InitSchedule(ctx context.Context) error {
|
func (s *CertificateService) InitSchedule(ctx context.Context) error {
|
||||||
|
// 每日发送过期证书提醒
|
||||||
app.GetScheduler().MustAdd("certificateExpireSoonNotify", "0 0 * * *", func() {
|
app.GetScheduler().MustAdd("certificateExpireSoonNotify", "0 0 * * *", func() {
|
||||||
certificates, err := s.certRepo.ListExpireSoon(context.Background())
|
certificates, err := s.certificateRepo.ListExpireSoon(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.GetLogger().Error("failed to get certificates which expire soon", "err", err)
|
app.GetLogger().Error("failed to get certificates which expire soon", "err", err)
|
||||||
return
|
return
|
||||||
@@ -56,11 +66,37 @@ func (s *CertificateService) InitSchedule(ctx context.Context) error {
|
|||||||
app.GetLogger().Error("failed to send notification", "err", err)
|
app.GetLogger().Error("failed to send notification", "err", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 每日清理过期证书
|
||||||
|
app.GetScheduler().MustAdd("certificateExpiredCleanup", "0 0 * * *", func() {
|
||||||
|
settings, err := s.settingsRepo.GetByName(ctx, "persistence")
|
||||||
|
if err != nil {
|
||||||
|
app.GetLogger().Error("failed to get persistence settings", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var settingsContent *domain.PersistenceSettingsContent
|
||||||
|
json.Unmarshal([]byte(settings.Content), &settingsContent)
|
||||||
|
if settingsContent != nil && settingsContent.ExpiredCertificatesMaxDaysRetention != 0 {
|
||||||
|
ret, err := s.certificateRepo.DeleteWhere(
|
||||||
|
context.Background(),
|
||||||
|
dbx.NewExp(fmt.Sprintf("expireAt<DATETIME('now', '-%d days')", settingsContent.ExpiredCertificatesMaxDaysRetention)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
app.GetLogger().Error("failed to delete expired certificates", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret > 0 {
|
||||||
|
app.GetLogger().Info(fmt.Sprintf("cleanup %d expired certificates", ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.CertificateArchiveFileReq) (*dtos.CertificateArchiveFileResp, error) {
|
func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.CertificateArchiveFileReq) (*dtos.CertificateArchiveFileResp, error) {
|
||||||
certificate, err := s.certRepo.GetById(ctx, req.CertificateId)
|
certificate, err := s.certificateRepo.GetById(ctx, req.CertificateId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -109,7 +145,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
|
|||||||
{
|
{
|
||||||
const pfxPassword = "certimate"
|
const pfxPassword = "certimate"
|
||||||
|
|
||||||
certPFX, err := certs.TransformCertificateFromPEMToPFX(certificate.Certificate, certificate.PrivateKey, pfxPassword)
|
certPFX, err := certutil.TransformCertificateFromPEMToPFX(certificate.Certificate, certificate.PrivateKey, pfxPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -147,7 +183,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
|
|||||||
{
|
{
|
||||||
const jksPassword = "certimate"
|
const jksPassword = "certimate"
|
||||||
|
|
||||||
certJKS, err := certs.TransformCertificateFromPEMToJKS(certificate.Certificate, certificate.PrivateKey, jksPassword, jksPassword, jksPassword)
|
certJKS, err := certutil.TransformCertificateFromPEMToJKS(certificate.Certificate, certificate.PrivateKey, jksPassword, jksPassword, jksPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -187,7 +223,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CertificateService) ValidateCertificate(ctx context.Context, req *dtos.CertificateValidateCertificateReq) (*dtos.CertificateValidateCertificateResp, error) {
|
func (s *CertificateService) ValidateCertificate(ctx context.Context, req *dtos.CertificateValidateCertificateReq) (*dtos.CertificateValidateCertificateResp, error) {
|
||||||
certX509, err := certs.ParseCertificateFromPEM(req.Certificate)
|
certX509, err := certutil.ParseCertificateFromPEM(req.Certificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if time.Now().After(certX509.NotAfter) {
|
} else if time.Now().After(certX509.NotAfter) {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package deployer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,60 +14,59 @@ type Deployer interface {
|
|||||||
Deploy(ctx context.Context) error
|
Deploy(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type deployerOptions struct {
|
type DeployerWithWorkflowNodeConfig struct {
|
||||||
Provider domain.DeployProviderType
|
Node *domain.WorkflowNode
|
||||||
ProviderAccessConfig map[string]any
|
Logger *slog.Logger
|
||||||
ProviderDeployConfig map[string]any
|
CertificatePEM string
|
||||||
|
PrivateKeyPEM string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
|
func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error) {
|
||||||
Certificate string
|
if config.Node == nil {
|
||||||
PrivateKey string
|
return nil, fmt.Errorf("node is nil")
|
||||||
},
|
}
|
||||||
) (Deployer, error) {
|
if config.Node.Type != domain.WorkflowNodeTypeDeploy {
|
||||||
if node.Type != domain.WorkflowNodeTypeDeploy {
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeDeploy))
|
||||||
return nil, fmt.Errorf("node type is not deploy")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig := node.GetConfigForDeploy()
|
nodeConfig := config.Node.GetConfigForDeploy()
|
||||||
|
options := &deployerProviderOptions{
|
||||||
|
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
|
||||||
|
ProviderAccessConfig: make(map[string]any),
|
||||||
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId)
|
if nodeConfig.ProviderAccessId != "" {
|
||||||
if err != nil {
|
access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId)
|
||||||
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
||||||
|
} else {
|
||||||
|
options.ProviderAccessConfig = access.Config
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessConfig, err := access.UnmarshalConfigToMap()
|
deployerProvider, err := createDeployerProvider(options)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deployer, err := createDeployer(&deployerOptions{
|
|
||||||
Provider: domain.DeployProviderType(nodeConfig.Provider),
|
|
||||||
ProviderAccessConfig: accessConfig,
|
|
||||||
ProviderDeployConfig: nodeConfig.ProviderConfig,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyDeployer{
|
return &deployerImpl{
|
||||||
logger: logger.NewNilLogger(),
|
provider: deployerProvider.WithLogger(config.Logger),
|
||||||
deployer: deployer,
|
certPEM: config.CertificatePEM,
|
||||||
deployCertificate: certdata.Certificate,
|
privkeyPEM: config.PrivateKeyPEM,
|
||||||
deployPrivateKey: certdata.PrivateKey,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
|
type deployerImpl struct {
|
||||||
type proxyDeployer struct {
|
provider deployer.Deployer
|
||||||
logger logger.Logger
|
certPEM string
|
||||||
deployer deployer.Deployer
|
privkeyPEM string
|
||||||
deployCertificate string
|
|
||||||
deployPrivateKey string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *proxyDeployer) Deploy(ctx context.Context) error {
|
var _ Deployer = (*deployerImpl)(nil)
|
||||||
_, err := d.deployer.Deploy(ctx, d.deployCertificate, d.deployPrivateKey)
|
|
||||||
|
func (d *deployerImpl) Deploy(ctx context.Context) error {
|
||||||
|
_, err := d.provider.Deploy(ctx, d.certPEM, d.privkeyPEM)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -9,19 +8,11 @@ const CollectionNameAccess = "access"
|
|||||||
|
|
||||||
type Access struct {
|
type Access struct {
|
||||||
Meta
|
Meta
|
||||||
Name string `json:"name" db:"name"`
|
Name string `json:"name" db:"name"`
|
||||||
Provider string `json:"provider" db:"provider"`
|
Provider string `json:"provider" db:"provider"`
|
||||||
Config string `json:"config" db:"config"`
|
Config map[string]any `json:"config" db:"config"`
|
||||||
DeletedAt *time.Time `json:"deleted" db:"deleted"`
|
Reserve string `json:"reserve,omitempty" db:"reserve"`
|
||||||
}
|
DeletedAt *time.Time `json:"deleted" db:"deleted"`
|
||||||
|
|
||||||
func (a *Access) UnmarshalConfigToMap() (map[string]any, error) {
|
|
||||||
config := make(map[string]any)
|
|
||||||
if err := json.Unmarshal([]byte(a.Config), &config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigFor1Panel struct {
|
type AccessConfigFor1Panel struct {
|
||||||
@@ -74,18 +65,24 @@ type AccessConfigForBytePlus struct {
|
|||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForBunny struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForCacheFly struct {
|
type AccessConfigForCacheFly struct {
|
||||||
ApiToken string `json:"apiToken"`
|
ApiToken string `json:"apiToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForCdnfly struct {
|
type AccessConfigForCdnfly struct {
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
ApiSecret string `json:"apiSecret"`
|
ApiSecret string `json:"apiSecret"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForCloudflare struct {
|
type AccessConfigForCloudflare struct {
|
||||||
DnsApiToken string `json:"dnsApiToken"`
|
DnsApiToken string `json:"dnsApiToken"`
|
||||||
|
ZoneApiToken string `json:"zoneApiToken,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForClouDNS struct {
|
type AccessConfigForClouDNS struct {
|
||||||
@@ -98,6 +95,15 @@ type AccessConfigForCMCCCloud struct {
|
|||||||
AccessKeySecret string `json:"accessKeySecret"`
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForDeSEC struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForDingTalkBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForDNSLA struct {
|
type AccessConfigForDNSLA struct {
|
||||||
ApiId string `json:"apiId"`
|
ApiId string `json:"apiId"`
|
||||||
ApiSecret string `json:"apiSecret"`
|
ApiSecret string `json:"apiSecret"`
|
||||||
@@ -108,11 +114,25 @@ type AccessConfigForDogeCloud struct {
|
|||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForDynv6 struct {
|
||||||
|
HttpToken string `json:"httpToken"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForEdgio struct {
|
type AccessConfigForEdgio struct {
|
||||||
ClientId string `json:"clientId"`
|
ClientId string `json:"clientId"`
|
||||||
ClientSecret string `json:"clientSecret"`
|
ClientSecret string `json:"clientSecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForEmail struct {
|
||||||
|
SmtpHost string `json:"smtpHost"`
|
||||||
|
SmtpPort int32 `json:"smtpPort"`
|
||||||
|
SmtpTls bool `json:"smtpTls"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
DefaultSenderAddress string `json:"defaultSenderAddress,omitempty"`
|
||||||
|
DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForGcore struct {
|
type AccessConfigForGcore struct {
|
||||||
ApiToken string `json:"apiToken"`
|
ApiToken string `json:"apiToken"`
|
||||||
}
|
}
|
||||||
@@ -127,6 +147,19 @@ type AccessConfigForGoDaddy struct {
|
|||||||
ApiSecret string `json:"apiSecret"`
|
ApiSecret string `json:"apiSecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForGoEdge struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiRole string `json:"apiRole"`
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForGoogleTrustServices struct {
|
||||||
|
EabKid string `json:"eabKid"`
|
||||||
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForHuaweiCloud struct {
|
type AccessConfigForHuaweiCloud struct {
|
||||||
AccessKeyId string `json:"accessKeyId"`
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
SecretAccessKey string `json:"secretAccessKey"`
|
SecretAccessKey string `json:"secretAccessKey"`
|
||||||
@@ -141,7 +174,16 @@ type AccessConfigForKubernetes struct {
|
|||||||
KubeConfig string `json:"kubeConfig,omitempty"`
|
KubeConfig string `json:"kubeConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForLocal struct{}
|
type AccessConfigForLarkBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForMattermost struct {
|
||||||
|
ServerUrl string `json:"serverUrl"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
DefaultChannelId string `json:"defaultChannelId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForNamecheap struct {
|
type AccessConfigForNamecheap struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -157,13 +199,36 @@ type AccessConfigForNameSilo struct {
|
|||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForNetcup struct {
|
||||||
|
CustomerNumber string `json:"customerNumber"`
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
ApiPassword string `json:"apiPassword"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForNetlify struct {
|
||||||
|
ApiToken string `json:"apiToken"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForNS1 struct {
|
type AccessConfigForNS1 struct {
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForPorkbun struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
SecretApiKey string `json:"secretApiKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForPowerDNS struct {
|
type AccessConfigForPowerDNS struct {
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForProxmoxVE struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiToken string `json:"apiToken"`
|
||||||
|
ApiTokenSecret string `json:"apiTokenSecret,omitempty"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForQiniu struct {
|
type AccessConfigForQiniu struct {
|
||||||
@@ -190,6 +255,16 @@ type AccessConfigForSSH struct {
|
|||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForSSLCom struct {
|
||||||
|
EabKid string `json:"eabKid"`
|
||||||
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForTelegram struct {
|
||||||
|
BotToken string `json:"botToken"`
|
||||||
|
DefaultChatId int64 `json:"defaultChatId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForTencentCloud struct {
|
type AccessConfigForTencentCloud struct {
|
||||||
SecretId string `json:"secretId"`
|
SecretId string `json:"secretId"`
|
||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
@@ -201,17 +276,46 @@ type AccessConfigForUCloud struct {
|
|||||||
ProjectId string `json:"projectId,omitempty"`
|
ProjectId string `json:"projectId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForUpyun struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForVercel struct {
|
||||||
|
ApiAccessToken string `json:"apiAccessToken"`
|
||||||
|
TeamId string `json:"teamId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForVolcEngine struct {
|
type AccessConfigForVolcEngine struct {
|
||||||
AccessKeyId string `json:"accessKeyId"`
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
SecretAccessKey string `json:"secretAccessKey"`
|
SecretAccessKey string `json:"secretAccessKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForWangsu struct {
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForWebhook struct {
|
type AccessConfigForWebhook struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
Method string `json:"method,omitempty"`
|
||||||
|
HeadersString string `json:"headers,omitempty"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
DefaultDataForDeployment string `json:"defaultDataForDeployment,omitempty"`
|
||||||
|
DefaultDataForNotification string `json:"defaultDataForNotification,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessConfigForWeComBot struct {
|
||||||
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForWestcn struct {
|
type AccessConfigForWestcn struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
ApiPassword string `json:"password"`
|
ApiPassword string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForZeroSSL struct {
|
||||||
|
EabKid string `json:"eabKid"`
|
||||||
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/certs"
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CollectionNameCertificate = "certificate"
|
const CollectionNameCertificate = "certificate"
|
||||||
@@ -39,19 +42,58 @@ func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate
|
|||||||
c.EffectAt = certX509.NotBefore
|
c.EffectAt = certX509.NotBefore
|
||||||
c.ExpireAt = certX509.NotAfter
|
c.ExpireAt = certX509.NotAfter
|
||||||
|
|
||||||
switch certX509.SignatureAlgorithm {
|
switch certX509.PublicKeyAlgorithm {
|
||||||
case x509.SHA256WithRSA, x509.SHA256WithRSAPSS:
|
case x509.RSA:
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA2048
|
{
|
||||||
case x509.SHA384WithRSA, x509.SHA384WithRSAPSS:
|
len := 0
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA3072
|
if pubkey, ok := certX509.PublicKey.(*rsa.PublicKey); ok {
|
||||||
case x509.SHA512WithRSA, x509.SHA512WithRSAPSS:
|
len = pubkey.N.BitLen()
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA4096
|
}
|
||||||
case x509.ECDSAWithSHA256:
|
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC256
|
switch len {
|
||||||
case x509.ECDSAWithSHA384:
|
case 0:
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC384
|
c.KeyAlgorithm = CertificateKeyAlgorithmType("RSA")
|
||||||
case x509.ECDSAWithSHA512:
|
case 2048:
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC512
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA2048
|
||||||
|
case 3072:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA3072
|
||||||
|
case 4096:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA4096
|
||||||
|
case 8192:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA8192
|
||||||
|
default:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmType(fmt.Sprintf("RSA%d", len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case x509.ECDSA:
|
||||||
|
{
|
||||||
|
len := 0
|
||||||
|
if pubkey, ok := certX509.PublicKey.(*ecdsa.PublicKey); ok {
|
||||||
|
if pubkey.Curve != nil && pubkey.Curve.Params() != nil {
|
||||||
|
len = pubkey.Curve.Params().BitSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len {
|
||||||
|
case 0:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmType("EC")
|
||||||
|
case 256:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC256
|
||||||
|
case 384:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC384
|
||||||
|
case 521:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC512
|
||||||
|
default:
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmType(fmt.Sprintf("EC%d", len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case x509.Ed25519:
|
||||||
|
{
|
||||||
|
c.KeyAlgorithm = CertificateKeyAlgorithmType("ED25519")
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
c.KeyAlgorithm = CertificateKeyAlgorithmType("")
|
c.KeyAlgorithm = CertificateKeyAlgorithmType("")
|
||||||
}
|
}
|
||||||
@@ -63,10 +105,10 @@ func (c *Certificate) PopulateFromPEM(certPEM, privkeyPEM string) *Certificate {
|
|||||||
c.Certificate = certPEM
|
c.Certificate = certPEM
|
||||||
c.PrivateKey = privkeyPEM
|
c.PrivateKey = privkeyPEM
|
||||||
|
|
||||||
_, issuerCertPEM, _ := certs.ExtractCertificatesFromPEM(certPEM)
|
_, issuerCertPEM, _ := certutil.ExtractCertificatesFromPEM(certPEM)
|
||||||
c.IssuerCertificate = issuerCertPEM
|
c.IssuerCertificate = issuerCertPEM
|
||||||
|
|
||||||
certX509, _ := certs.ParseCertificateFromPEM(certPEM)
|
certX509, _ := certutil.ParseCertificateFromPEM(certPEM)
|
||||||
if certX509 != nil {
|
if certX509 != nil {
|
||||||
c.PopulateFromX509(certX509)
|
c.PopulateFromX509(certX509)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package dtos
|
package dtos
|
||||||
|
|
||||||
type CertificateArchiveFileReq struct {
|
type CertificateArchiveFileReq struct {
|
||||||
CertificateId string `json:"-"`
|
CertificateId string `json:"-"`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package dtos
|
package dtos
|
||||||
|
|
||||||
import "github.com/usual2970/certimate/internal/domain"
|
import "github.com/usual2970/certimate/internal/domain"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package dtos
|
package dtos
|
||||||
|
|
||||||
import "github.com/usual2970/certimate/internal/domain"
|
import "github.com/usual2970/certimate/internal/domain"
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,16 @@ type NotifyChannelType string
|
|||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
const (
|
const (
|
||||||
NotifyChannelTypeBark = NotifyChannelType("bark")
|
NotifyChannelTypeBark = NotifyChannelType("bark")
|
||||||
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")
|
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")
|
||||||
NotifyChannelTypeEmail = NotifyChannelType("email")
|
NotifyChannelTypeEmail = NotifyChannelType("email")
|
||||||
|
NotifyChannelTypeGotify = NotifyChannelType("gotify")
|
||||||
NotifyChannelTypeLark = NotifyChannelType("lark")
|
NotifyChannelTypeLark = NotifyChannelType("lark")
|
||||||
|
NotifyChannelTypeMattermost = NotifyChannelType("mattermost")
|
||||||
|
NotifyChannelTypePushover = NotifyChannelType("pushover")
|
||||||
|
NotifyChannelTypePushPlus = NotifyChannelType("pushplus")
|
||||||
NotifyChannelTypeServerChan = NotifyChannelType("serverchan")
|
NotifyChannelTypeServerChan = NotifyChannelType("serverchan")
|
||||||
NotifyChannelTypeTelegram = NotifyChannelType("telegram")
|
NotifyChannelTypeTelegram = NotifyChannelType("telegram")
|
||||||
NotifyChannelTypeWebhook = NotifyChannelType("webhook")
|
NotifyChannelTypeWebhook = NotifyChannelType("webhook")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
type AccessProviderType string
|
type AccessProviderType string
|
||||||
|
|
||||||
@@ -9,160 +9,257 @@ type AccessProviderType string
|
|||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
AccessProviderType1Panel = AccessProviderType("1panel")
|
AccessProviderType1Panel = AccessProviderType("1panel")
|
||||||
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
AccessProviderTypeACMECA = AccessProviderType("acmeca") // ACME CA(预留)
|
||||||
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
||||||
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
||||||
AccessProviderTypeAWS = AccessProviderType("aws")
|
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
||||||
AccessProviderTypeAzure = AccessProviderType("azure")
|
AccessProviderTypeAWS = AccessProviderType("aws")
|
||||||
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
|
AccessProviderTypeAzure = AccessProviderType("azure")
|
||||||
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
|
||||||
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
||||||
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
||||||
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
|
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
||||||
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
|
AccessProviderTypeBunny = AccessProviderType("bunny")
|
||||||
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
|
AccessProviderTypeBuypass = AccessProviderType("buypass")
|
||||||
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
|
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
|
||||||
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
|
||||||
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
|
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
|
||||||
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
|
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
|
||||||
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
|
||||||
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留)
|
||||||
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留)
|
||||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
AccessProviderTypeDeSEC = AccessProviderType("desec")
|
||||||
AccessProviderTypeGname = AccessProviderType("gname")
|
AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot")
|
||||||
AccessProviderTypeGcore = AccessProviderType("gcore")
|
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
|
||||||
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
|
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
|
||||||
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留)
|
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
||||||
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
||||||
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
AccessProviderTypeEmail = AccessProviderType("email")
|
||||||
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||||
AccessProviderTypeLocal = AccessProviderType("local")
|
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") // FlexCDN(预留)
|
||||||
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
AccessProviderTypeGname = AccessProviderType("gname")
|
||||||
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
|
AccessProviderTypeGcore = AccessProviderType("gcore")
|
||||||
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
|
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
|
||||||
AccessProviderTypeNS1 = AccessProviderType("ns1")
|
AccessProviderTypeGoEdge = AccessProviderType("goedge")
|
||||||
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
|
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
|
||||||
AccessProviderTypeQiniu = AccessProviderType("qiniu")
|
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
|
||||||
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
|
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
|
||||||
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
AccessProviderTypeKubernetes = AccessProviderType("k8s")
|
||||||
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
||||||
AccessProviderTypeSSH = AccessProviderType("ssh")
|
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
||||||
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
|
||||||
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
AccessProviderTypeLeCDN = AccessProviderType("lecdn") // LeCDN(预留)
|
||||||
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
|
AccessProviderTypeLocal = AccessProviderType("local")
|
||||||
AccessProviderTypeWebhook = AccessProviderType("webhook")
|
AccessProviderTypeMattermost = AccessProviderType("mattermost")
|
||||||
AccessProviderTypeWestcn = AccessProviderType("westcn")
|
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
||||||
|
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
|
||||||
|
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
|
||||||
|
AccessProviderTypeNetcup = AccessProviderType("netcup")
|
||||||
|
AccessProviderTypeNetlify = AccessProviderType("netlify")
|
||||||
|
AccessProviderTypeNS1 = AccessProviderType("ns1")
|
||||||
|
AccessProviderTypePorkbun = AccessProviderType("porkbun")
|
||||||
|
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
|
||||||
|
AccessProviderTypeProxmoxVE = AccessProviderType("proxmoxve")
|
||||||
|
AccessProviderTypeQiniu = AccessProviderType("qiniu")
|
||||||
|
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
|
||||||
|
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
||||||
|
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
||||||
|
AccessProviderTypeSSH = AccessProviderType("ssh")
|
||||||
|
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
|
||||||
|
AccessProviderTypeTelegram = AccessProviderType("telegram")
|
||||||
|
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
||||||
|
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
||||||
|
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
||||||
|
AccessProviderTypeVercel = AccessProviderType("vercel")
|
||||||
|
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
|
||||||
|
AccessProviderTypeWangsu = AccessProviderType("wangsu")
|
||||||
|
AccessProviderTypeWebhook = AccessProviderType("webhook")
|
||||||
|
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
|
||||||
|
AccessProviderTypeWestcn = AccessProviderType("westcn")
|
||||||
|
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyDNSProviderType string
|
type CAProviderType string
|
||||||
|
|
||||||
/*
|
/*
|
||||||
申请证书 DNS 提供商常量值。
|
证书颁发机构提供商常量值。
|
||||||
短横线前的部分始终等于授权提供商类型。
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
ApplyDNSProviderTypeACMEHttpReq = ApplyDNSProviderType("acmehttpreq")
|
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
|
||||||
ApplyDNSProviderTypeAliyun = ApplyDNSProviderType("aliyun") // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS]
|
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
|
||||||
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns")
|
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
|
||||||
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
|
CAProviderTypeLetsEncryptStaging = CAProviderType(AccessProviderTypeLetsEncryptStaging)
|
||||||
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
|
CAProviderTypeSSLCom = CAProviderType(AccessProviderTypeSSLCOM)
|
||||||
ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
|
CAProviderTypeZeroSSL = CAProviderType(AccessProviderTypeZeroSSL)
|
||||||
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
|
|
||||||
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
|
|
||||||
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns")
|
|
||||||
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
|
|
||||||
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
|
|
||||||
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud")
|
|
||||||
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla")
|
|
||||||
ApplyDNSProviderTypeGcore = ApplyDNSProviderType("gcore")
|
|
||||||
ApplyDNSProviderTypeGname = ApplyDNSProviderType("gname")
|
|
||||||
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
|
|
||||||
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
|
|
||||||
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
|
|
||||||
ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS]
|
|
||||||
ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns")
|
|
||||||
ApplyDNSProviderTypeNamecheap = ApplyDNSProviderType("namecheap")
|
|
||||||
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
|
|
||||||
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
|
|
||||||
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
|
|
||||||
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns")
|
|
||||||
ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun")
|
|
||||||
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
|
|
||||||
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns")
|
|
||||||
ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS]
|
|
||||||
ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns")
|
|
||||||
ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployProviderType string
|
type ACMEDns01ProviderType string
|
||||||
|
|
||||||
/*
|
/*
|
||||||
部署目标提供商常量值。
|
ACME DNS-01 提供商常量值。
|
||||||
短横线前的部分始终等于授权提供商类型。
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
DeployProviderType1PanelConsole = DeployProviderType("1panel-console")
|
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
|
||||||
DeployProviderType1PanelSite = DeployProviderType("1panel-site")
|
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
|
||||||
DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb")
|
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
|
||||||
DeployProviderTypeAliyunCASDeploy = DeployProviderType("aliyun-casdeploy")
|
ACMEDns01ProviderTypeAliyunESA = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-esa")
|
||||||
DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn")
|
ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53]
|
||||||
DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb")
|
ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53")
|
||||||
DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn")
|
ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure]
|
||||||
DeployProviderTypeAliyunESA = DeployProviderType("aliyun-esa")
|
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
|
||||||
DeployProviderTypeAliyunFC = DeployProviderType("aliyun-fc")
|
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
|
||||||
DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live")
|
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
|
||||||
DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb")
|
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
|
||||||
DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss")
|
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
|
||||||
DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod")
|
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
|
||||||
DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf")
|
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
|
||||||
DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront")
|
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
|
||||||
DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn")
|
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
|
||||||
DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn")
|
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
|
||||||
DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console")
|
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
|
||||||
DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site")
|
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
|
||||||
DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn")
|
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
|
||||||
DeployProviderTypeCacheFly = DeployProviderType("cachefly")
|
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
|
||||||
DeployProviderTypeCdnfly = DeployProviderType("cdnfly")
|
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
|
||||||
DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn")
|
ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS]
|
||||||
DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications")
|
ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns")
|
||||||
DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn")
|
ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap)
|
||||||
DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn")
|
ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom)
|
||||||
DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb")
|
ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo)
|
||||||
DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf")
|
ACMEDns01ProviderTypeNetcup = ACMEDns01ProviderType(AccessProviderTypeNetcup)
|
||||||
DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb")
|
ACMEDns01ProviderTypeNetlify = ACMEDns01ProviderType(AccessProviderTypeNetlify)
|
||||||
DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn")
|
ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1)
|
||||||
DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live")
|
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
|
||||||
DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod")
|
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
|
||||||
DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret")
|
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
|
||||||
DeployProviderTypeLocal = DeployProviderType("local")
|
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
|
||||||
DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn")
|
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
|
||||||
DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili")
|
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||||
DeployProviderTypeSafeLine = DeployProviderType("safeline")
|
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||||
DeployProviderTypeSSH = DeployProviderType("ssh")
|
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||||
DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn")
|
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||||
DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb")
|
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
|
||||||
DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos")
|
)
|
||||||
DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css")
|
|
||||||
DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn")
|
type DeploymentProviderType string
|
||||||
DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo")
|
|
||||||
DeployProviderTypeTencentCloudSCF = DeployProviderType("tencentcloud-scf")
|
/*
|
||||||
DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy")
|
部署证书主机提供商常量值。
|
||||||
DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod")
|
短横线前的部分始终等于授权提供商类型。
|
||||||
DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf")
|
|
||||||
DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn")
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3")
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn")
|
*/
|
||||||
DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb")
|
const (
|
||||||
DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn")
|
DeploymentProviderType1PanelConsole = DeploymentProviderType(AccessProviderType1Panel + "-console")
|
||||||
DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex")
|
DeploymentProviderType1PanelSite = DeploymentProviderType(AccessProviderType1Panel + "-site")
|
||||||
DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live")
|
DeploymentProviderTypeAliyunALB = DeploymentProviderType(AccessProviderTypeAliyun + "-alb")
|
||||||
DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos")
|
DeploymentProviderTypeAliyunAPIGW = DeploymentProviderType(AccessProviderTypeAliyun + "-apigw")
|
||||||
DeployProviderTypeWebhook = DeployProviderType("webhook")
|
DeploymentProviderTypeAliyunCAS = DeploymentProviderType(AccessProviderTypeAliyun + "-cas")
|
||||||
|
DeploymentProviderTypeAliyunCASDeploy = DeploymentProviderType(AccessProviderTypeAliyun + "-casdeploy")
|
||||||
|
DeploymentProviderTypeAliyunCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-cdn")
|
||||||
|
DeploymentProviderTypeAliyunCLB = DeploymentProviderType(AccessProviderTypeAliyun + "-clb")
|
||||||
|
DeploymentProviderTypeAliyunDCDN = DeploymentProviderType(AccessProviderTypeAliyun + "-dcdn")
|
||||||
|
DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos")
|
||||||
|
DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa")
|
||||||
|
DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc")
|
||||||
|
DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga") // 阿里云全球加速(预留)
|
||||||
|
DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live")
|
||||||
|
DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb")
|
||||||
|
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
|
||||||
|
DeploymentProviderTypeAliyunVOD = DeploymentProviderType(AccessProviderTypeAliyun + "-vod")
|
||||||
|
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
|
||||||
|
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
|
||||||
|
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
|
||||||
|
DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault")
|
||||||
|
DeploymentProviderTypeBaiduCloudAppBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-appblb")
|
||||||
|
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
|
||||||
|
DeploymentProviderTypeBaiduCloudCDN = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cdn")
|
||||||
|
DeploymentProviderTypeBaiduCloudCert = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cert")
|
||||||
|
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
|
||||||
|
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
|
||||||
|
DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site")
|
||||||
|
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
|
||||||
|
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
|
||||||
|
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
|
||||||
|
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
|
||||||
|
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
|
||||||
|
DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications")
|
||||||
|
DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) // FlexCDN(预留)
|
||||||
|
DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn")
|
||||||
|
DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge)
|
||||||
|
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
|
||||||
|
DeploymentProviderTypeHuaweiCloudELB = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-elb")
|
||||||
|
DeploymentProviderTypeHuaweiCloudSCM = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-scm")
|
||||||
|
DeploymentProviderTypeHuaweiCloudWAF = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-waf")
|
||||||
|
DeploymentProviderTypeJDCloudALB = DeploymentProviderType(AccessProviderTypeJDCloud + "-alb")
|
||||||
|
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")
|
||||||
|
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
||||||
|
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
||||||
|
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
||||||
|
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) // LeCDN(预留)
|
||||||
|
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
||||||
|
DeploymentProviderTypeNetlifySite = DeploymentProviderType(AccessProviderTypeNetlify + "-site")
|
||||||
|
DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE)
|
||||||
|
DeploymentProviderTypeQiniuCDN = DeploymentProviderType(AccessProviderTypeQiniu + "-cdn")
|
||||||
|
DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo")
|
||||||
|
DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili")
|
||||||
|
DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn")
|
||||||
|
DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine)
|
||||||
|
DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH)
|
||||||
|
DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn")
|
||||||
|
DeploymentProviderTypeTencentCloudCLB = DeploymentProviderType(AccessProviderTypeTencentCloud + "-clb")
|
||||||
|
DeploymentProviderTypeTencentCloudCOS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cos")
|
||||||
|
DeploymentProviderTypeTencentCloudCSS = DeploymentProviderType(AccessProviderTypeTencentCloud + "-css")
|
||||||
|
DeploymentProviderTypeTencentCloudECDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ecdn")
|
||||||
|
DeploymentProviderTypeTencentCloudEO = DeploymentProviderType(AccessProviderTypeTencentCloud + "-eo")
|
||||||
|
DeploymentProviderTypeTencentCloudSCF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-scf")
|
||||||
|
DeploymentProviderTypeTencentCloudSSL = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssl")
|
||||||
|
DeploymentProviderTypeTencentCloudSSLDeploy = DeploymentProviderType(AccessProviderTypeTencentCloud + "-ssldeploy")
|
||||||
|
DeploymentProviderTypeTencentCloudVOD = DeploymentProviderType(AccessProviderTypeTencentCloud + "-vod")
|
||||||
|
DeploymentProviderTypeTencentCloudWAF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-waf")
|
||||||
|
DeploymentProviderTypeUCloudUCDN = DeploymentProviderType(AccessProviderTypeUCloud + "-ucdn")
|
||||||
|
DeploymentProviderTypeUCloudUS3 = DeploymentProviderType(AccessProviderTypeUCloud + "-us3")
|
||||||
|
DeploymentProviderTypeUpyunCDN = DeploymentProviderType(AccessProviderTypeUpyun + "-cdn")
|
||||||
|
DeploymentProviderTypeUpyunFile = DeploymentProviderType(AccessProviderTypeUpyun + "-file")
|
||||||
|
DeploymentProviderTypeVolcEngineALB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-alb")
|
||||||
|
DeploymentProviderTypeVolcEngineCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-cdn")
|
||||||
|
DeploymentProviderTypeVolcEngineCertCenter = DeploymentProviderType(AccessProviderTypeVolcEngine + "-certcenter")
|
||||||
|
DeploymentProviderTypeVolcEngineCLB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-clb")
|
||||||
|
DeploymentProviderTypeVolcEngineDCDN = DeploymentProviderType(AccessProviderTypeVolcEngine + "-dcdn")
|
||||||
|
DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex")
|
||||||
|
DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live")
|
||||||
|
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
|
||||||
|
DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn") // 网宿 CDN(预留)
|
||||||
|
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
|
||||||
|
DeploymentProviderTypeWangsuCert = DeploymentProviderType(AccessProviderTypeWangsu + "-cert") // 网宿证书管理(预留)
|
||||||
|
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationProviderType string
|
||||||
|
|
||||||
|
/*
|
||||||
|
消息通知提供商常量值。
|
||||||
|
短横线前的部分始终等于授权提供商类型。
|
||||||
|
|
||||||
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
NotificationProviderTypeDingTalkBot = NotificationProviderType(AccessProviderTypeDingTalkBot)
|
||||||
|
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
|
||||||
|
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
|
||||||
|
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
|
||||||
|
NotificationProviderTypeTelegram = NotificationProviderType(AccessProviderTypeTelegram)
|
||||||
|
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
|
||||||
|
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,17 +13,18 @@ type Settings struct {
|
|||||||
Content string `json:"content" db:"content"`
|
Content string `json:"content" db:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyTemplatesSettingsContent struct {
|
type NotifyTemplatesSettingsContent struct {
|
||||||
NotifyTemplates []NotifyTemplate `json:"notifyTemplates"`
|
NotifyTemplates []struct {
|
||||||
}
|
Subject string `json:"subject"`
|
||||||
|
Message string `json:"message"`
|
||||||
type NotifyTemplate struct {
|
} `json:"notifyTemplates"`
|
||||||
Subject string `json:"subject"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyChannelsSettingsContent map[string]map[string]any
|
type NotifyChannelsSettingsContent map[string]map[string]any
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) {
|
func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) {
|
||||||
conf := &NotifyChannelsSettingsContent{}
|
conf := &NotifyChannelsSettingsContent{}
|
||||||
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {
|
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {
|
||||||
@@ -37,3 +38,8 @@ func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error
|
|||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PersistenceSettingsContent struct {
|
||||||
|
WorkflowRunsMaxDaysRetention int `json:"workflowRunsMaxDaysRetention"`
|
||||||
|
ExpiredCertificatesMaxDaysRetention int `json:"expiredCertificatesMaxDaysRetention"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package domain
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/maps"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CollectionNameWorkflow = "workflow"
|
const CollectionNameWorkflow = "workflow"
|
||||||
@@ -62,19 +62,23 @@ type WorkflowNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeConfigForApply struct {
|
type WorkflowNodeConfigForApply struct {
|
||||||
Domains string `json:"domains"` // 域名列表,以半角逗号分隔
|
Domains string `json:"domains"` // 域名列表,以半角分号分隔
|
||||||
ContactEmail string `json:"contactEmail"` // 联系邮箱
|
ContactEmail string `json:"contactEmail"` // 联系邮箱
|
||||||
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
|
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
|
||||||
Provider string `json:"provider"` // DNS 提供商
|
Provider string `json:"provider"` // DNS 提供商
|
||||||
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
|
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
|
||||||
ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
|
ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
|
||||||
KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法
|
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值将使用全局配置)
|
||||||
Nameservers string `json:"nameservers"` // DNS 服务器列表,以半角逗号分隔
|
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
||||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout"` // DNS 传播超时时间(零值取决于提供商的默认值)
|
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
||||||
DnsTTL int32 `json:"dnsTTL"` // DNS TTL(零值取决于提供商的默认值)
|
KeyAlgorithm string `json:"keyAlgorithm"` // 证书算法
|
||||||
DisableFollowCNAME bool `json:"disableFollowCNAME"` // 是否关闭 CNAME 跟随
|
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
||||||
DisableARI bool `json:"disableARI"` // 是否关闭 ARI
|
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
|
||||||
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays"` // 证书到期前多少天前跳过续期(零值将使用默认值 30)
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播检查超时时间(零值取决于提供商的默认值)
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS 解析记录 TTL(零值取决于提供商的默认值)
|
||||||
|
DisableFollowCNAME bool `json:"disableFollowCNAME,omitempty"` // 是否关闭 CNAME 跟随
|
||||||
|
DisableARI bool `json:"disableARI,omitempty"` // 是否关闭 ARI
|
||||||
|
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays,omitempty"` // 证书到期前多少天前跳过续期(零值将使用默认值 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeConfigForUpload struct {
|
type WorkflowNodeConfigForUpload struct {
|
||||||
@@ -84,86 +88,74 @@ type WorkflowNodeConfigForUpload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeConfigForDeploy struct {
|
type WorkflowNodeConfigForDeploy struct {
|
||||||
Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate”
|
Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate”
|
||||||
Provider string `json:"provider"` // 主机提供商
|
Provider string `json:"provider"` // 主机提供商
|
||||||
ProviderAccessId string `json:"providerAccessId"` // 主机提供商授权记录 ID
|
ProviderAccessId string `json:"providerAccessId,omitempty"` // 主机提供商授权记录 ID
|
||||||
ProviderConfig map[string]any `json:"providerConfig"` // 主机提供商额外配置
|
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 主机提供商额外配置
|
||||||
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过
|
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeConfigForNotify struct {
|
type WorkflowNodeConfigForNotify struct {
|
||||||
Channel string `json:"channel"` // 通知渠道
|
Channel string `json:"channel,omitempty"` // Deprecated: v0.4.x 将废弃
|
||||||
Subject string `json:"subject"` // 通知主题
|
Provider string `json:"provider"` // 通知提供商
|
||||||
Message string `json:"message"` // 通知内容
|
ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID
|
||||||
}
|
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
|
||||||
|
Subject string `json:"subject"` // 通知主题
|
||||||
func (n *WorkflowNode) getConfigValueAsString(key string) string {
|
Message string `json:"message"` // 通知内容
|
||||||
return maps.GetValueAsString(n.Config, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *WorkflowNode) getConfigValueAsBool(key string) bool {
|
|
||||||
return maps.GetValueAsBool(n.Config, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *WorkflowNode) getConfigValueAsInt32(key string) int32 {
|
|
||||||
return maps.GetValueAsInt32(n.Config, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *WorkflowNode) getConfigValueAsMap(key string) map[string]any {
|
|
||||||
if val, ok := n.Config[key]; ok {
|
|
||||||
if result, ok := val.(map[string]any); ok {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return make(map[string]any)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
||||||
skipBeforeExpiryDays := n.getConfigValueAsInt32("skipBeforeExpiryDays")
|
skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
|
||||||
if skipBeforeExpiryDays == 0 {
|
if skipBeforeExpiryDays == 0 {
|
||||||
skipBeforeExpiryDays = 30
|
skipBeforeExpiryDays = 30
|
||||||
}
|
}
|
||||||
|
|
||||||
return WorkflowNodeConfigForApply{
|
return WorkflowNodeConfigForApply{
|
||||||
Domains: n.getConfigValueAsString("domains"),
|
Domains: maputil.GetString(n.Config, "domains"),
|
||||||
ContactEmail: n.getConfigValueAsString("contactEmail"),
|
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
|
||||||
Provider: n.getConfigValueAsString("provider"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
ProviderConfig: n.getConfigValueAsMap("providerConfig"),
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
KeyAlgorithm: n.getConfigValueAsString("keyAlgorithm"),
|
CAProvider: maputil.GetString(n.Config, "caProvider"),
|
||||||
Nameservers: n.getConfigValueAsString("nameservers"),
|
CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"),
|
||||||
DnsPropagationTimeout: n.getConfigValueAsInt32("dnsPropagationTimeout"),
|
CAProviderConfig: maputil.GetKVMapAny(n.Config, "caProviderConfig"),
|
||||||
DnsTTL: n.getConfigValueAsInt32("dnsTTL"),
|
KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"),
|
||||||
DisableFollowCNAME: n.getConfigValueAsBool("disableFollowCNAME"),
|
Nameservers: maputil.GetString(n.Config, "nameservers"),
|
||||||
DisableARI: n.getConfigValueAsBool("disableARI"),
|
DnsPropagationWait: maputil.GetInt32(n.Config, "dnsPropagationWait"),
|
||||||
|
DnsPropagationTimeout: maputil.GetInt32(n.Config, "dnsPropagationTimeout"),
|
||||||
|
DnsTTL: maputil.GetInt32(n.Config, "dnsTTL"),
|
||||||
|
DisableFollowCNAME: maputil.GetBool(n.Config, "disableFollowCNAME"),
|
||||||
|
DisableARI: maputil.GetBool(n.Config, "disableARI"),
|
||||||
SkipBeforeExpiryDays: skipBeforeExpiryDays,
|
SkipBeforeExpiryDays: skipBeforeExpiryDays,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForUpload() WorkflowNodeConfigForUpload {
|
func (n *WorkflowNode) GetConfigForUpload() WorkflowNodeConfigForUpload {
|
||||||
return WorkflowNodeConfigForUpload{
|
return WorkflowNodeConfigForUpload{
|
||||||
Certificate: n.getConfigValueAsString("certificate"),
|
Certificate: maputil.GetString(n.Config, "certificate"),
|
||||||
PrivateKey: n.getConfigValueAsString("privateKey"),
|
PrivateKey: maputil.GetString(n.Config, "privateKey"),
|
||||||
Domains: n.getConfigValueAsString("domains"),
|
Domains: maputil.GetString(n.Config, "domains"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
|
func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
|
||||||
return WorkflowNodeConfigForDeploy{
|
return WorkflowNodeConfigForDeploy{
|
||||||
Certificate: n.getConfigValueAsString("certificate"),
|
Certificate: maputil.GetString(n.Config, "certificate"),
|
||||||
Provider: n.getConfigValueAsString("provider"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
ProviderConfig: n.getConfigValueAsMap("providerConfig"),
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
SkipOnLastSucceeded: n.getConfigValueAsBool("skipOnLastSucceeded"),
|
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
|
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
|
||||||
return WorkflowNodeConfigForNotify{
|
return WorkflowNodeConfigForNotify{
|
||||||
Channel: n.getConfigValueAsString("channel"),
|
Channel: maputil.GetString(n.Config, "channel"),
|
||||||
Subject: n.getConfigValueAsString("subject"),
|
Provider: maputil.GetString(n.Config, "provider"),
|
||||||
Message: n.getConfigValueAsString("message"),
|
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
|
||||||
|
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
|
||||||
|
Subject: maputil.GetString(n.Config, "subject"),
|
||||||
|
Message: maputil.GetString(n.Config, "message"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
internal/domain/workflow_log.go
Normal file
30
internal/domain/workflow_log.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
const CollectionNameWorkflowLog = "workflow_logs"
|
||||||
|
|
||||||
|
type WorkflowLog struct {
|
||||||
|
Meta
|
||||||
|
WorkflowId string `json:"workflowId" db:"workflowId"`
|
||||||
|
RunId string `json:"workflorunIdwId" db:"runId"`
|
||||||
|
NodeId string `json:"nodeId" db:"nodeId"`
|
||||||
|
NodeName string `json:"nodeName" db:"nodeName"`
|
||||||
|
Timestamp int64 `json:"timestamp" db:"timestamp"` // 毫秒级时间戳
|
||||||
|
Level string `json:"level" db:"level"`
|
||||||
|
Message string `json:"message" db:"message"`
|
||||||
|
Data map[string]any `json:"data" db:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkflowLogs []WorkflowLog
|
||||||
|
|
||||||
|
func (r WorkflowLogs) ErrorString() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, log := range r {
|
||||||
|
if log.Level == "ERROR" {
|
||||||
|
builder.WriteString(log.Message)
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(builder.String())
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ type WorkflowRun struct {
|
|||||||
Trigger WorkflowTriggerType `json:"trigger" db:"trigger"`
|
Trigger WorkflowTriggerType `json:"trigger" db:"trigger"`
|
||||||
StartedAt time.Time `json:"startedAt" db:"startedAt"`
|
StartedAt time.Time `json:"startedAt" db:"startedAt"`
|
||||||
EndedAt time.Time `json:"endedAt" db:"endedAt"`
|
EndedAt time.Time `json:"endedAt" db:"endedAt"`
|
||||||
Logs []WorkflowRunLog `json:"logs" db:"logs"`
|
Detail *WorkflowNode `json:"detail" db:"detail"`
|
||||||
Error string `json:"error" db:"error"`
|
Error string `json:"error" db:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,39 +26,3 @@ const (
|
|||||||
WorkflowRunStatusTypeFailed WorkflowRunStatusType = "failed"
|
WorkflowRunStatusTypeFailed WorkflowRunStatusType = "failed"
|
||||||
WorkflowRunStatusTypeCanceled WorkflowRunStatusType = "canceled"
|
WorkflowRunStatusTypeCanceled WorkflowRunStatusType = "canceled"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WorkflowRunLog struct {
|
|
||||||
NodeId string `json:"nodeId"`
|
|
||||||
NodeName string `json:"nodeName"`
|
|
||||||
Records []WorkflowRunLogRecord `json:"records"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkflowRunLogRecord struct {
|
|
||||||
Time string `json:"time"`
|
|
||||||
Level WorkflowRunLogLevel `json:"level"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkflowRunLogLevel string
|
|
||||||
|
|
||||||
const (
|
|
||||||
WorkflowRunLogLevelDebug WorkflowRunLogLevel = "DEBUG"
|
|
||||||
WorkflowRunLogLevelInfo WorkflowRunLogLevel = "INFO"
|
|
||||||
WorkflowRunLogLevelWarn WorkflowRunLogLevel = "WARN"
|
|
||||||
WorkflowRunLogLevelError WorkflowRunLogLevel = "ERROR"
|
|
||||||
)
|
|
||||||
|
|
||||||
type WorkflowRunLogs []WorkflowRunLog
|
|
||||||
|
|
||||||
func (r WorkflowRunLogs) ErrorString() string {
|
|
||||||
var builder strings.Builder
|
|
||||||
for _, log := range r {
|
|
||||||
if log.Error != "" {
|
|
||||||
builder.WriteString(log.Error)
|
|
||||||
builder.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|||||||
72
internal/notify/notifier.go
Normal file
72
internal/notify/notifier.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Notifier interface {
|
||||||
|
Notify(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotifierWithWorkflowNodeConfig struct {
|
||||||
|
Node *domain.WorkflowNode
|
||||||
|
Logger *slog.Logger
|
||||||
|
Subject string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error) {
|
||||||
|
if config.Node == nil {
|
||||||
|
return nil, fmt.Errorf("node is nil")
|
||||||
|
}
|
||||||
|
if config.Node.Type != domain.WorkflowNodeTypeNotify {
|
||||||
|
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeNotify))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeConfig := config.Node.GetConfigForNotify()
|
||||||
|
options := ¬ifierProviderOptions{
|
||||||
|
Provider: domain.NotificationProviderType(nodeConfig.Provider),
|
||||||
|
ProviderAccessConfig: make(map[string]any),
|
||||||
|
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
accessRepo := repository.NewAccessRepository()
|
||||||
|
if nodeConfig.ProviderAccessId != "" {
|
||||||
|
access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
|
||||||
|
} else {
|
||||||
|
options.ProviderAccessConfig = access.Config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifierProvider, err := createNotifierProvider(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ¬ifierImpl{
|
||||||
|
provider: notifierProvider.WithLogger(config.Logger),
|
||||||
|
subject: config.Subject,
|
||||||
|
message: config.Message,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type notifierImpl struct {
|
||||||
|
provider notifier.Notifier
|
||||||
|
subject string
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Notifier = (*notifierImpl)(nil)
|
||||||
|
|
||||||
|
func (n *notifierImpl) Notify(ctx context.Context) error {
|
||||||
|
_, err := n.provider.Notify(ctx, n.subject, n.message)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -9,10 +9,11 @@ import (
|
|||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/maps"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func SendToAllChannels(subject, message string) error {
|
func SendToAllChannels(subject, message string) error {
|
||||||
notifiers, err := getEnabledNotifiers()
|
notifiers, err := getEnabledNotifiers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -38,8 +39,9 @@ func SendToAllChannels(subject, message string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func SendToChannel(subject, message string, channel string, channelConfig map[string]any) error {
|
func SendToChannel(subject, message string, channel string, channelConfig map[string]any) error {
|
||||||
notifier, err := createNotifier(domain.NotifyChannelType(channel), channelConfig)
|
notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(channel), channelConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -48,6 +50,7 @@ func SendToChannel(subject, message string, channel string, channelConfig map[st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
||||||
settingsRepo := repository.NewSettingsRepository()
|
settingsRepo := repository.NewSettingsRepository()
|
||||||
settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels")
|
settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels")
|
||||||
@@ -62,11 +65,11 @@ func getEnabledNotifiers() ([]notifier.Notifier, error) {
|
|||||||
|
|
||||||
notifiers := make([]notifier.Notifier, 0)
|
notifiers := make([]notifier.Notifier, 0)
|
||||||
for k, v := range rs {
|
for k, v := range rs {
|
||||||
if !maps.GetValueAsBool(v, "enabled") {
|
if !maputil.GetBool(v, "enabled") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
notifier, err := createNotifier(domain.NotifyChannelType(k), v)
|
notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(k), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,76 +2,152 @@ package notify
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
|
||||||
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
||||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
||||||
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
||||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
||||||
"github.com/usual2970/certimate/internal/pkg/utils/maps"
|
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
|
||||||
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) {
|
type notifierProviderOptions struct {
|
||||||
|
Provider domain.NotificationProviderType
|
||||||
|
ProviderAccessConfig map[string]any
|
||||||
|
ProviderExtendedConfig map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) {
|
||||||
/*
|
/*
|
||||||
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
switch channel {
|
switch options.Provider {
|
||||||
case domain.NotifyChannelTypeBark:
|
case domain.NotificationProviderTypeDingTalkBot:
|
||||||
return pBark.NewNotifier(&pBark.NotifierConfig{
|
{
|
||||||
DeviceKey: maps.GetValueAsString(channelConfig, "deviceKey"),
|
access := domain.AccessConfigForDingTalkBot{}
|
||||||
ServerUrl: maps.GetValueAsString(channelConfig, "serverUrl"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
})
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeDingTalk:
|
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
||||||
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
WebhookUrl: access.WebhookUrl,
|
||||||
AccessToken: maps.GetValueAsString(channelConfig, "accessToken"),
|
Secret: access.Secret,
|
||||||
Secret: maps.GetValueAsString(channelConfig, "secret"),
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeEmail:
|
case domain.NotificationProviderTypeEmail:
|
||||||
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
{
|
||||||
SmtpHost: maps.GetValueAsString(channelConfig, "smtpHost"),
|
access := domain.AccessConfigForEmail{}
|
||||||
SmtpPort: maps.GetValueAsInt32(channelConfig, "smtpPort"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
SmtpTLS: maps.GetValueOrDefaultAsBool(channelConfig, "smtpTLS", true),
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
Username: maps.GetValueOrDefaultAsString(channelConfig, "username", maps.GetValueAsString(channelConfig, "senderAddress")),
|
}
|
||||||
Password: maps.GetValueAsString(channelConfig, "password"),
|
|
||||||
SenderAddress: maps.GetValueAsString(channelConfig, "senderAddress"),
|
|
||||||
ReceiverAddress: maps.GetValueAsString(channelConfig, "receiverAddress"),
|
|
||||||
})
|
|
||||||
|
|
||||||
case domain.NotifyChannelTypeLark:
|
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
||||||
return pLark.NewNotifier(&pLark.NotifierConfig{
|
SmtpHost: access.SmtpHost,
|
||||||
WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"),
|
SmtpPort: access.SmtpPort,
|
||||||
})
|
SmtpTls: access.SmtpTls,
|
||||||
|
Username: access.Username,
|
||||||
|
Password: access.Password,
|
||||||
|
SenderAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "senderAddress", access.DefaultSenderAddress),
|
||||||
|
ReceiverAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "receiverAddress", access.DefaultReceiverAddress),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeServerChan:
|
case domain.NotificationProviderTypeLarkBot:
|
||||||
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
{
|
||||||
Url: maps.GetValueAsString(channelConfig, "url"),
|
access := domain.AccessConfigForLarkBot{}
|
||||||
})
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeTelegram:
|
return pLark.NewNotifier(&pLark.NotifierConfig{
|
||||||
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
WebhookUrl: access.WebhookUrl,
|
||||||
ApiToken: maps.GetValueAsString(channelConfig, "apiToken"),
|
})
|
||||||
ChatId: maps.GetValueAsInt64(channelConfig, "chatId"),
|
}
|
||||||
})
|
|
||||||
|
|
||||||
case domain.NotifyChannelTypeWebhook:
|
case domain.NotificationProviderTypeMattermost:
|
||||||
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
{
|
||||||
Url: maps.GetValueAsString(channelConfig, "url"),
|
access := domain.AccessConfigForMattermost{}
|
||||||
AllowInsecureConnections: maps.GetValueAsBool(channelConfig, "allowInsecureConnections"),
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
})
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case domain.NotifyChannelTypeWeCom:
|
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
|
||||||
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
ServerUrl: access.ServerUrl,
|
||||||
WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"),
|
Username: access.Username,
|
||||||
})
|
Password: access.Password,
|
||||||
|
ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.NotificationProviderTypeTelegram:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForTelegram{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
||||||
|
BotToken: access.BotToken,
|
||||||
|
ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.NotificationProviderTypeWebhook:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForWebhook{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedHeaders := make(map[string]string)
|
||||||
|
if defaultHeadersString := access.HeadersString; defaultHeadersString != "" {
|
||||||
|
h, err := httputil.ParseHeaders(defaultHeadersString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
||||||
|
}
|
||||||
|
for key := range h {
|
||||||
|
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" {
|
||||||
|
h, err := httputil.ParseHeaders(extendedHeadersString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
||||||
|
}
|
||||||
|
for key := range h {
|
||||||
|
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
||||||
|
WebhookUrl: access.Url,
|
||||||
|
WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification),
|
||||||
|
Method: access.Method,
|
||||||
|
Headers: mergedHeaders,
|
||||||
|
AllowInsecureConnections: access.AllowInsecureConnections,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case domain.NotificationProviderTypeWeComBot:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForWeComBot{}
|
||||||
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
||||||
|
WebhookUrl: access.WebhookUrl,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unsupported notifier channel: %s", channelConfig)
|
return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider)
|
||||||
}
|
}
|
||||||
|
|||||||
108
internal/notify/providers_deprecated.go
Normal file
108
internal/notify/providers_deprecated.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
|
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
||||||
|
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
||||||
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
|
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
||||||
|
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
||||||
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
|
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
||||||
|
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
||||||
|
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
||||||
|
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
||||||
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
|
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
||||||
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
|
func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) {
|
||||||
|
/*
|
||||||
|
注意:如果追加新的常量值,请保持以 ASCII 排序。
|
||||||
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
|
*/
|
||||||
|
switch channel {
|
||||||
|
case domain.NotifyChannelTypeBark:
|
||||||
|
return pBark.NewNotifier(&pBark.NotifierConfig{
|
||||||
|
DeviceKey: maputil.GetString(channelConfig, "deviceKey"),
|
||||||
|
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeDingTalk:
|
||||||
|
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
||||||
|
WebhookUrl: "https://oapi.dingtalk.com/robot/send?access_token=" + maputil.GetString(channelConfig, "accessToken"),
|
||||||
|
Secret: maputil.GetString(channelConfig, "secret"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeEmail:
|
||||||
|
return pEmail.NewNotifier(&pEmail.NotifierConfig{
|
||||||
|
SmtpHost: maputil.GetString(channelConfig, "smtpHost"),
|
||||||
|
SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"),
|
||||||
|
SmtpTls: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true),
|
||||||
|
Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")),
|
||||||
|
Password: maputil.GetString(channelConfig, "password"),
|
||||||
|
SenderAddress: maputil.GetString(channelConfig, "senderAddress"),
|
||||||
|
ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeGotify:
|
||||||
|
return pGotify.NewNotifier(&pGotify.NotifierConfig{
|
||||||
|
Url: maputil.GetString(channelConfig, "url"),
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeLark:
|
||||||
|
return pLark.NewNotifier(&pLark.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeMattermost:
|
||||||
|
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
|
||||||
|
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
|
||||||
|
ChannelId: maputil.GetString(channelConfig, "channelId"),
|
||||||
|
Username: maputil.GetString(channelConfig, "username"),
|
||||||
|
Password: maputil.GetString(channelConfig, "password"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypePushover:
|
||||||
|
return pPushover.NewNotifier(&pPushover.NotifierConfig{
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
User: maputil.GetString(channelConfig, "user"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypePushPlus:
|
||||||
|
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
|
||||||
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeServerChan:
|
||||||
|
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
||||||
|
Url: maputil.GetString(channelConfig, "url"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeTelegram:
|
||||||
|
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
||||||
|
BotToken: maputil.GetString(channelConfig, "apiToken"),
|
||||||
|
ChatId: maputil.GetInt64(channelConfig, "chatId"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeWebhook:
|
||||||
|
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "url"),
|
||||||
|
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"),
|
||||||
|
})
|
||||||
|
|
||||||
|
case domain.NotifyChannelTypeWeCom:
|
||||||
|
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
||||||
|
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unsupported notifier channel '%s'", channelConfig)
|
||||||
|
}
|
||||||
@@ -8,25 +8,30 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/domain/dtos"
|
"github.com/usual2970/certimate/internal/domain/dtos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
const (
|
const (
|
||||||
notifyTestTitle = "测试通知"
|
notifyTestTitle = "测试通知"
|
||||||
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
|
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type settingsRepository interface {
|
type settingsRepository interface {
|
||||||
GetByName(ctx context.Context, name string) (*domain.Settings, error)
|
GetByName(ctx context.Context, name string) (*domain.Settings, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
type NotifyService struct {
|
type NotifyService struct {
|
||||||
settingsRepo settingsRepository
|
settingsRepo settingsRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func NewNotifyService(settingsRepo settingsRepository) *NotifyService {
|
func NewNotifyService(settingsRepo settingsRepository) *NotifyService {
|
||||||
return &NotifyService{
|
return &NotifyService{
|
||||||
settingsRepo: settingsRepo,
|
settingsRepo: settingsRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: v0.4.x 将废弃
|
||||||
func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error {
|
func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error {
|
||||||
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package acmehttpreq
|
package acmehttpreq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package aliyunesa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
|
||||||
|
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := internal.NewDefaultConfig()
|
||||||
|
providerConfig.SecretID = config.AccessKeyId
|
||||||
|
providerConfig.SecretKey = config.AccessKeySecret
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = config.DnsTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
package lego_aliyunesa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
aliesa "github.com/alibabacloud-go/esa-20240910/v2/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "ALICLOUDESA_"
|
||||||
|
|
||||||
|
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||||
|
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||||
|
EnvRegionID = envNamespace + "REGION_ID"
|
||||||
|
|
||||||
|
EnvTTL = envNamespace + "TTL"
|
||||||
|
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||||
|
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SecretID string
|
||||||
|
SecretKey string
|
||||||
|
RegionID string
|
||||||
|
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int32
|
||||||
|
HTTPTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSProvider struct {
|
||||||
|
client *aliesa.Client
|
||||||
|
config *Config
|
||||||
|
|
||||||
|
siteIDs map[string]int64
|
||||||
|
siteIDsMtx sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||||
|
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(EnvAccessKey, EnvSecretKey, EnvRegionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("alicloud-esa: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.SecretID = values[EnvAccessKey]
|
||||||
|
config.SecretKey = values[EnvSecretKey]
|
||||||
|
config.RegionID = values[EnvRegionID]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("alicloud-esa: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliesa.NewClient(&aliopen.Config{
|
||||||
|
AccessKeyId: tea.String(config.SecretID),
|
||||||
|
AccessKeySecret: tea.String(config.SecretKey),
|
||||||
|
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", config.RegionID)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("alicloud-esa: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{
|
||||||
|
client: client,
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
siteId, err := d.getSiteId(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.addOrUpdateDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
siteId, err := d.getSiteId(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.removeDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, ".")); err != nil {
|
||||||
|
return fmt.Errorf("alicloud-esa: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||||
|
d.siteIDsMtx.Lock()
|
||||||
|
siteID, ok := d.siteIDs[siteName]
|
||||||
|
d.siteIDsMtx.Unlock()
|
||||||
|
if ok {
|
||||||
|
return siteID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNumber := 1
|
||||||
|
pageSize := 500
|
||||||
|
for {
|
||||||
|
request := &aliesa.ListSitesRequest{
|
||||||
|
SiteName: tea.String(siteName),
|
||||||
|
PageNumber: tea.Int32(int32(pageNumber)),
|
||||||
|
PageSize: tea.Int32(int32(pageNumber)),
|
||||||
|
AccessType: tea.String("NS"),
|
||||||
|
}
|
||||||
|
response, err := d.client.ListSites(request)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Body == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
for _, record := range response.Body.Sites {
|
||||||
|
if tea.StringValue(record.SiteName) == siteName {
|
||||||
|
d.siteIDsMtx.Lock()
|
||||||
|
d.siteIDs[siteName] = *record.SiteId
|
||||||
|
d.siteIDsMtx.Unlock()
|
||||||
|
return *record.SiteId, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.Body.Sites) < pageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNumber++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, errors.New("failed to get site id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) {
|
||||||
|
pageNumber := 1
|
||||||
|
pageSize := 500
|
||||||
|
for {
|
||||||
|
request := &aliesa.ListRecordsRequest{
|
||||||
|
SiteId: tea.Int64(siteId),
|
||||||
|
Type: tea.String("TXT"),
|
||||||
|
RecordName: tea.String(effectiveFQDN),
|
||||||
|
PageNumber: tea.Int32(int32(pageNumber)),
|
||||||
|
PageSize: tea.Int32(int32(pageNumber)),
|
||||||
|
}
|
||||||
|
response, err := d.client.ListRecords(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Body == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
for _, record := range response.Body.Records {
|
||||||
|
if tea.StringValue(record.RecordName) == effectiveFQDN {
|
||||||
|
return record, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.Body.Records) < pageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNumber++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) addOrUpdateDNSRecord(siteId int64, effectiveFQDN, value string) error {
|
||||||
|
record, err := d.findDNSRecord(siteId, effectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
request := &aliesa.CreateRecordRequest{
|
||||||
|
SiteId: tea.Int64(siteId),
|
||||||
|
Type: tea.String("TXT"),
|
||||||
|
RecordName: tea.String(effectiveFQDN),
|
||||||
|
Data: &aliesa.CreateRecordRequestData{
|
||||||
|
Value: tea.String(value),
|
||||||
|
},
|
||||||
|
Ttl: tea.Int32(d.config.TTL),
|
||||||
|
}
|
||||||
|
_, err := d.client.CreateRecord(request)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
request := &aliesa.UpdateRecordRequest{
|
||||||
|
RecordId: record.RecordId,
|
||||||
|
Ttl: tea.Int32(d.config.TTL),
|
||||||
|
Data: &aliesa.UpdateRecordRequestData{
|
||||||
|
Value: tea.String(value),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := d.client.UpdateRecord(request)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) removeDNSRecord(siteId int64, effectiveFQDN string) error {
|
||||||
|
record, err := d.findDNSRecord(siteId, effectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
request := &aliesa.DeleteRecordRequest{
|
||||||
|
RecordId: record.RecordId,
|
||||||
|
}
|
||||||
|
_, err = d.client.DeleteRecord(request)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/azuredns"
|
"github.com/go-acme/lego/v4/providers/dns/azuredns"
|
||||||
|
|
||||||
azcommon "github.com/usual2970/certimate/internal/pkg/vendors/azure-sdk/common"
|
azcommon "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChallengeProviderConfig struct {
|
type ChallengeProviderConfig struct {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lego_baiducloud
|
package lego_baiducloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
bceDns "github.com/baidubce/bce-sdk-go/services/dns"
|
bcedns "github.com/baidubce/bce-sdk-go/services/dns"
|
||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/platform/config/env"
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
@@ -38,7 +38,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
client *bceDns.Client
|
client *bcedns.Client
|
||||||
config *Config
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||||||
return nil, errors.New("baiducloud: the configuration of the DNS provider is nil")
|
return nil, errors.New("baiducloud: the configuration of the DNS provider is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := bceDns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
|
client, err := bcedns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +89,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("baiducloud: %w", err)
|
return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -109,7 +109,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("baiducloud: %w", err)
|
return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -128,11 +128,11 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bceDns.Record, error) {
|
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) {
|
||||||
pageMarker := ""
|
pageMarker := ""
|
||||||
pageSize := 1000
|
pageSize := 1000
|
||||||
for {
|
for {
|
||||||
request := &bceDns.ListRecordRequest{}
|
request := &bcedns.ListRecordRequest{}
|
||||||
request.Rr = subDomain
|
request.Rr = subDomain
|
||||||
request.Marker = pageMarker
|
request.Marker = pageMarker
|
||||||
request.MaxKeys = pageSize
|
request.MaxKeys = pageSize
|
||||||
@@ -159,13 +159,13 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bceDns.Record,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||||
record, err := d.getDNSRecord(zoneName, subDomain)
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if record == nil {
|
if record == nil {
|
||||||
request := &bceDns.CreateRecordRequest{
|
request := &bcedns.CreateRecordRequest{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
Rr: subDomain,
|
Rr: subDomain,
|
||||||
Value: value,
|
Value: value,
|
||||||
@@ -174,7 +174,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
err := d.client.CreateRecord(zoneName, request, d.generateClientToken())
|
err := d.client.CreateRecord(zoneName, request, d.generateClientToken())
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
request := &bceDns.UpdateRecordRequest{
|
request := &bcedns.UpdateRecordRequest{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
Rr: subDomain,
|
Rr: subDomain,
|
||||||
Value: value,
|
Value: value,
|
||||||
@@ -186,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||||
record, err := d.getDNSRecord(zoneName, subDomain)
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package bunny
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/bunny"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := bunny.NewDefaultConfig()
|
||||||
|
providerConfig.APIKey = config.ApiKey
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := bunny.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
type ChallengeProviderConfig struct {
|
type ChallengeProviderConfig struct {
|
||||||
DnsApiToken string `json:"dnsApiToken"`
|
DnsApiToken string `json:"dnsApiToken"`
|
||||||
|
ZoneApiToken string `json:"zoneApiToken,omitempty"`
|
||||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -20,6 +21,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
|
|||||||
|
|
||||||
providerConfig := cloudflare.NewDefaultConfig()
|
providerConfig := cloudflare.NewDefaultConfig()
|
||||||
providerConfig.AuthToken = config.DnsApiToken
|
providerConfig.AuthToken = config.DnsApiToken
|
||||||
|
providerConfig.ZoneToken = config.ZoneApiToken
|
||||||
if config.DnsPropagationTimeout != 0 {
|
if config.DnsPropagationTimeout != 0 {
|
||||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cmccecloud: %w", err)
|
return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
|
||||||
@@ -106,34 +106,35 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if record == nil {
|
if record == nil {
|
||||||
// add new record
|
|
||||||
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{
|
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{
|
||||||
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
|
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
|
||||||
LineId: "0", // 默认线路
|
LineId: "0", // 默认线路
|
||||||
Rr: subDomain,
|
Rr: subDomain,
|
||||||
DomainName: readDomain,
|
DomainName: readDomain,
|
||||||
Description: "from certimate",
|
Description: "certimate acme",
|
||||||
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
|
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
|
||||||
Value: info.Value,
|
Value: info.Value,
|
||||||
Ttl: &d.config.TTL,
|
Ttl: &d.config.TTL,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("lego: %w", err)
|
return fmt.Errorf("cmccecloud: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.State != model.CreateRecordOpenapiResponseStateEnumOk {
|
if resp.State != model.CreateRecordOpenapiResponseStateEnumOk {
|
||||||
return fmt.Errorf("lego: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode)
|
return fmt.Errorf("cmccecloud: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// update record
|
|
||||||
resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{
|
resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{
|
||||||
ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{
|
ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{
|
||||||
RecordId: record.RecordId,
|
RecordId: record.RecordId,
|
||||||
Rr: subDomain,
|
Rr: subDomain,
|
||||||
DomainName: readDomain,
|
DomainName: readDomain,
|
||||||
Description: "from certmate",
|
Description: "certmate acme",
|
||||||
LineId: "0",
|
LineId: "0",
|
||||||
Type: model.ModifyRecordOpenapiBodyTypeEnumTxt,
|
Type: model.ModifyRecordOpenapiBodyTypeEnumTxt,
|
||||||
Value: info.Value,
|
Value: info.Value,
|
||||||
@@ -141,44 +142,52 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("lego: %w", err)
|
return fmt.Errorf("cmccecloud: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk {
|
if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk {
|
||||||
return fmt.Errorf("lego: create record failed, response state: %s", resp.State)
|
return fmt.Errorf("cmccecloud: create record failed, response state: %s", resp.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN)
|
zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cmccecloud: %w", err)
|
return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName)
|
subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cmccecloud: %w", err)
|
return fmt.Errorf("cmccecloud: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
readDomain := strings.Trim(zoneName, ".")
|
readDomain := strings.Trim(zoneName, ".")
|
||||||
record, err := d.getDomainRecord(readDomain, subDomain)
|
record, err := d.getDomainRecord(readDomain, subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if record == nil {
|
if record == nil {
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{
|
||||||
|
DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{
|
||||||
|
RecordIdList: []string{record.RecordId},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cmccecloud: %w", err)
|
||||||
|
}
|
||||||
|
if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk {
|
||||||
|
return fmt.Errorf("cmccecloud: delete record failed, unexpected response state: %s", resp.State)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{
|
|
||||||
DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{
|
|
||||||
RecordIdList: []string{record.RecordId},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("lego: %w", err)
|
|
||||||
}
|
|
||||||
if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk {
|
|
||||||
return fmt.Errorf("lego: delete record failed, response state: %s", resp.State)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +213,9 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco
|
|||||||
}
|
}
|
||||||
if resp.State != model.ListRecordOpenapiResponseStateEnumOk {
|
if resp.State != model.ListRecordOpenapiResponseStateEnumOk {
|
||||||
respStr, _ := json.Marshal(resp)
|
respStr, _ := json.Marshal(resp)
|
||||||
return nil, fmt.Errorf("request error. %s", string(respStr))
|
return nil, fmt.Errorf("cmccecloud: request error: %s", string(respStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Body.Data != nil {
|
if resp.Body.Data != nil {
|
||||||
for _, item := range *resp.Body.Data {
|
for _, item := range *resp.Body.Data {
|
||||||
if item.Rr == rr {
|
if item.Rr == rr {
|
||||||
@@ -213,9 +223,11 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages {
|
if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
page++
|
page++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package desec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/desec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := desec.NewDefaultConfig()
|
||||||
|
providerConfig.Token = config.Token
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := desec.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package lego_dnsla
|
package lego_dnsla
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/platform/config/env"
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
|
||||||
dnslasdk "github.com/usual2970/certimate/internal/pkg/vendors/dnsla-sdk"
|
dnslasdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dnsla"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -83,7 +83,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dnsla: %w", err)
|
return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -103,7 +103,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dnsla: %w", err)
|
return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package dynv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
|
||||||
|
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
HttpToken string `json:"httpToken"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := internal.NewDefaultConfig()
|
||||||
|
providerConfig.HTTPToken = config.HttpToken
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package lego_dynv6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
"github.com/libdns/dynv6"
|
||||||
|
"github.com/libdns/libdns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "DYNV6_"
|
||||||
|
|
||||||
|
EnvHTTPToken = envNamespace + "HTTP_TOKEN"
|
||||||
|
|
||||||
|
EnvTTL = envNamespace + "TTL"
|
||||||
|
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
HTTPToken string
|
||||||
|
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSProvider struct {
|
||||||
|
client *dynv6.Provider
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(EnvHTTPToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("dynv6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.HTTPToken = values[EnvHTTPToken]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("dynv6: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &dynv6.Provider{Token: config.HTTPToken}
|
||||||
|
|
||||||
|
return &DNSProvider{
|
||||||
|
client: client,
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dynv6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||||
|
return fmt.Errorf("dynv6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dynv6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||||
|
return fmt.Errorf("dynv6: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*libdns.Record, error) {
|
||||||
|
records, err := d.client.GetRecords(context.Background(), zoneName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
if record.Type == "TXT" && record.Name == subDomain {
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||||
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
record = &libdns.Record{
|
||||||
|
Type: "TXT",
|
||||||
|
Name: subDomain,
|
||||||
|
Value: value,
|
||||||
|
TTL: time.Duration(d.config.TTL) * time.Second,
|
||||||
|
}
|
||||||
|
_, err := d.client.AppendRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
record.Value = value
|
||||||
|
_, err := d.client.SetRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||||
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
_, err = d.client.DeleteRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package lego_gname
|
package lego_gname
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/platform/config/env"
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
|
||||||
gnamesdk "github.com/usual2970/certimate/internal/pkg/vendors/gname-sdk"
|
gnamesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/gname"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -82,7 +82,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gname: %w", err)
|
return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -102,7 +102,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gname: %w", err)
|
return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -121,9 +121,9 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) {
|
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) {
|
||||||
page := 1
|
page := int32(1)
|
||||||
pageSize := 20
|
pageSize := int32(20)
|
||||||
for {
|
for {
|
||||||
request := &gnamesdk.ListDomainResolutionRequest{}
|
request := &gnamesdk.ListDomainResolutionRequest{}
|
||||||
request.ZoneName = zoneName
|
request.ZoneName = zoneName
|
||||||
@@ -155,7 +155,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.Resolu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||||
record, err := d.getDNSRecord(zoneName, subDomain)
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -166,18 +166,19 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
RecordType: "TXT",
|
RecordType: "TXT",
|
||||||
RecordName: subDomain,
|
RecordName: subDomain,
|
||||||
RecordValue: value,
|
RecordValue: value,
|
||||||
TTL: d.config.TTL,
|
TTL: int32(d.config.TTL),
|
||||||
}
|
}
|
||||||
_, err := d.client.AddDomainResolution(request)
|
_, err := d.client.AddDomainResolution(request)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
recordId, _ := record.ID.Int64()
|
||||||
request := &gnamesdk.ModifyDomainResolutionRequest{
|
request := &gnamesdk.ModifyDomainResolutionRequest{
|
||||||
ID: record.ID,
|
ID: recordId,
|
||||||
ZoneName: zoneName,
|
ZoneName: zoneName,
|
||||||
RecordType: "TXT",
|
RecordType: "TXT",
|
||||||
RecordName: subDomain,
|
RecordName: subDomain,
|
||||||
RecordValue: value,
|
RecordValue: value,
|
||||||
TTL: d.config.TTL,
|
TTL: int32(d.config.TTL),
|
||||||
}
|
}
|
||||||
_, err := d.client.ModifyDomainResolution(request)
|
_, err := d.client.ModifyDomainResolution(request)
|
||||||
return err
|
return err
|
||||||
@@ -185,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||||
record, err := d.getDNSRecord(zoneName, subDomain)
|
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -194,9 +195,10 @@ func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordId, _ := record.ID.Int64()
|
||||||
request := &gnamesdk.DeleteDomainResolutionRequest{
|
request := &gnamesdk.DeleteDomainResolutionRequest{
|
||||||
ZoneName: zoneName,
|
ZoneName: zoneName,
|
||||||
RecordID: record.ID,
|
RecordID: recordId,
|
||||||
}
|
}
|
||||||
_, err = d.client.DeleteDomainResolution(request)
|
_, err = d.client.DeleteDomainResolution(request)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lego_jdcloud
|
package lego_jdcloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/platform/config/env"
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
|
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
|
||||||
jdDnsApi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
|
jddnsapi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
|
||||||
jdDnsClient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
|
jddnsclient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
|
||||||
jdDnsModel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
|
jddnsmodel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -41,7 +41,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
client *jdDnsClient.DomainserviceClient
|
client *jddnsclient.DomainserviceClient
|
||||||
config *Config
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +73,12 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||||||
return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
|
return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientCredentials := jdCore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
|
clientCredentials := jdcore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
|
||||||
client := jdDnsClient.NewDomainserviceClient(clientCredentials)
|
client := jddnsclient.NewDomainserviceClient(clientCredentials)
|
||||||
clientConfig := &client.Config
|
clientConfig := &client.Config
|
||||||
clientConfig.SetTimeout(config.HTTPTimeout)
|
clientConfig.SetTimeout(config.HTTPTimeout)
|
||||||
client.SetConfig(clientConfig)
|
client.SetConfig(clientConfig)
|
||||||
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
|
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
client: client,
|
client: client,
|
||||||
@@ -91,7 +91,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("jdcloud: %w", err)
|
return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -111,7 +111,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
|
|
||||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("jdcloud: %w", err)
|
return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||||
@@ -130,11 +130,11 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error) {
|
func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error) {
|
||||||
pageNumber := 1
|
pageNumber := 1
|
||||||
pageSize := 10
|
pageSize := 10
|
||||||
for {
|
for {
|
||||||
request := jdDnsApi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
request := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
||||||
request.SetDomainName(zoneName)
|
request.SetDomainName(zoneName)
|
||||||
|
|
||||||
response, err := d.client.DescribeDomains(request)
|
response, err := d.client.DescribeDomains(request)
|
||||||
@@ -158,7 +158,7 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error
|
|||||||
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) {
|
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jddnsmodel.DomainInfo, *jddnsmodel.RRInfo, error) {
|
||||||
zone, err := d.getDNSZone(zoneName)
|
zone, err := d.getDNSZone(zoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -167,7 +167,7 @@ func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsMod
|
|||||||
pageNumber := 1
|
pageNumber := 1
|
||||||
pageSize := 10
|
pageSize := 10
|
||||||
for {
|
for {
|
||||||
request := jdDnsApi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
|
request := jddnsapi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
|
||||||
request.SetSearch(subDomain)
|
request.SetSearch(subDomain)
|
||||||
request.SetPageNumber(pageNumber)
|
request.SetPageNumber(pageNumber)
|
||||||
request.SetPageSize(pageSize)
|
request.SetPageSize(pageSize)
|
||||||
@@ -200,7 +200,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if record == nil {
|
if record == nil {
|
||||||
request := jdDnsApi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jdDnsModel.AddRR{
|
request := jddnsapi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jddnsmodel.AddRR{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
HostRecord: subDomain,
|
HostRecord: subDomain,
|
||||||
HostValue: value,
|
HostValue: value,
|
||||||
@@ -210,7 +210,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
|
|||||||
_, err := d.client.CreateResourceRecord(request)
|
_, err := d.client.CreateResourceRecord(request)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
request := jdDnsApi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jdDnsModel.UpdateRR{
|
request := jddnsapi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jddnsmodel.UpdateRR{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
HostRecord: subDomain,
|
HostRecord: subDomain,
|
||||||
HostValue: value,
|
HostValue: value,
|
||||||
@@ -231,7 +231,7 @@ func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
|||||||
if record == nil {
|
if record == nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
request := jdDnsApi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
|
request := jddnsapi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
|
||||||
_, err = d.client.DeleteResourceRecord(request)
|
_, err = d.client.DeleteResourceRecord(request)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package netcup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/netcup"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
CustomerNumber string `json:"customerNumber"`
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
ApiPassword string `json:"apiPassword"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := netcup.NewDefaultConfig()
|
||||||
|
providerConfig.Customer = config.CustomerNumber
|
||||||
|
providerConfig.Key = config.ApiKey
|
||||||
|
providerConfig.Password = config.ApiPassword
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := netcup.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package netcup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/netlify"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
ApiToken string `json:"apiToken"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := netlify.NewDefaultConfig()
|
||||||
|
providerConfig.Token = config.ApiToken
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := netlify.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package porkbun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/porkbun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
SecretApiKey string `json:"secretApiKey"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := porkbun.NewDefaultConfig()
|
||||||
|
providerConfig.APIKey = config.ApiKey
|
||||||
|
providerConfig.SecretAPIKey = config.SecretApiKey
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := porkbun.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package namesilo
|
package powerdns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -9,10 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ChallengeProviderConfig struct {
|
type ChallengeProviderConfig struct {
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
@@ -24,6 +27,13 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
|
|||||||
providerConfig := pdns.NewDefaultConfig()
|
providerConfig := pdns.NewDefaultConfig()
|
||||||
providerConfig.Host = host
|
providerConfig.Host = host
|
||||||
providerConfig.APIKey = config.ApiKey
|
providerConfig.APIKey = config.ApiKey
|
||||||
|
if config.AllowInsecureConnections {
|
||||||
|
providerConfig.HTTPClient.Transport = &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
if config.DnsPropagationTimeout != 0 {
|
if config.DnsPropagationTimeout != 0 {
|
||||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
package lego_tencentcloudeo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||||
|
teo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envNamespace = "TENCENTCLOUDEO_"
|
||||||
|
|
||||||
|
EnvSecretID = envNamespace + "SECRET_ID"
|
||||||
|
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||||
|
EnvZoneID = envNamespace + "ZONE_ID"
|
||||||
|
|
||||||
|
EnvTTL = envNamespace + "TTL"
|
||||||
|
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||||
|
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SecretID string
|
||||||
|
SecretKey string
|
||||||
|
ZoneID string
|
||||||
|
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int32
|
||||||
|
HTTPTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type DNSProvider struct {
|
||||||
|
client *teo.Client
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||||
|
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("tencentcloud-eo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.SecretID = values[EnvSecretID]
|
||||||
|
config.SecretKey = values[EnvSecretKey]
|
||||||
|
config.ZoneID = values[EnvSecretKey]
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("tencentcloud-eo: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
credential := common.NewCredential(config.SecretID, config.SecretKey)
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds()))
|
||||||
|
client, err := teo.NewClient(credential, "", cpf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{
|
||||||
|
client: client,
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
if err := d.addOrUpdateDNSRecord(strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil {
|
||||||
|
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||||
|
|
||||||
|
if err := d.removeDNSRecord(strings.TrimRight(info.EffectiveFQDN, ".")); err != nil {
|
||||||
|
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) findDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) {
|
||||||
|
pageOffset := 0
|
||||||
|
pageLimit := 1000
|
||||||
|
for {
|
||||||
|
request := teo.NewDescribeDnsRecordsRequest()
|
||||||
|
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||||
|
request.Offset = common.Int64Ptr(int64(pageOffset))
|
||||||
|
request.Limit = common.Int64Ptr(int64(pageLimit))
|
||||||
|
request.Filters = []*teo.AdvancedFilter{
|
||||||
|
{
|
||||||
|
Name: common.StringPtr("type"),
|
||||||
|
Values: []*string{common.StringPtr("TXT")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := d.client.DescribeDnsRecords(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Response == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
for _, record := range response.Response.DnsRecords {
|
||||||
|
if *record.Name == effectiveFQDN {
|
||||||
|
return record, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.Response.DnsRecords) < pageLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
pageOffset += len(response.Response.DnsRecords)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
|
||||||
|
record, err := d.findDNSRecord(effectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
request := teo.NewCreateDnsRecordRequest()
|
||||||
|
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||||
|
request.Name = common.StringPtr(effectiveFQDN)
|
||||||
|
request.Type = common.StringPtr("TXT")
|
||||||
|
request.Content = common.StringPtr(value)
|
||||||
|
request.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||||
|
_, err := d.client.CreateDnsRecord(request)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
record.Content = common.StringPtr(value)
|
||||||
|
record.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||||
|
request := teo.NewModifyDnsRecordsRequest()
|
||||||
|
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||||
|
request.DnsRecords = []*teo.DnsRecord{record}
|
||||||
|
if _, err := d.client.ModifyDnsRecords(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *record.Status == "disable" {
|
||||||
|
request := teo.NewModifyDnsRecordsStatusRequest()
|
||||||
|
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||||
|
request.RecordsToEnable = []*string{record.RecordId}
|
||||||
|
if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error {
|
||||||
|
record, err := d.findDNSRecord(effectiveFQDN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
request := teo.NewDeleteDnsRecordsRequest()
|
||||||
|
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||||
|
request.RecordIds = []*string{record.RecordId}
|
||||||
|
_, err = d.client.DeleteDnsRecords(request)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package tencentcloudeo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
|
||||||
|
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
SecretId string `json:"secretId"`
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
ZoneId string `json:"zoneId"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := internal.NewDefaultConfig()
|
||||||
|
providerConfig.SecretID = config.SecretId
|
||||||
|
providerConfig.SecretKey = config.SecretKey
|
||||||
|
providerConfig.ZoneID = config.ZoneId
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = config.DnsTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package vercel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/vercel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChallengeProviderConfig struct {
|
||||||
|
ApiAccessToken string `json:"apiAccessToken"`
|
||||||
|
TeamId string `json:"teamId,omitempty"`
|
||||||
|
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||||
|
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
providerConfig := vercel.NewDefaultConfig()
|
||||||
|
providerConfig.AuthToken = config.ApiAccessToken
|
||||||
|
providerConfig.TeamID = config.TeamId
|
||||||
|
if config.DnsPropagationTimeout != 0 {
|
||||||
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
|
}
|
||||||
|
if config.DnsTTL != 0 {
|
||||||
|
providerConfig.TTL = int(config.DnsTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := vercel.NewDNSProviderConfig(providerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -1,21 +1,26 @@
|
|||||||
package deployer
|
package deployer
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
// 表示定义证书部署器的抽象类型接口。
|
// 表示定义证书部署器的抽象类型接口。
|
||||||
// 注意与 `Uploader` 区分,“部署”通常为“上传”的后置操作。
|
// 注意与 `Uploader` 区分,“部署”通常为“上传”的后置操作。
|
||||||
type Deployer interface {
|
type Deployer interface {
|
||||||
|
WithLogger(logger *slog.Logger) Deployer
|
||||||
|
|
||||||
// 部署证书。
|
// 部署证书。
|
||||||
//
|
//
|
||||||
// 入参:
|
// 入参:
|
||||||
// - ctx:上下文。
|
// - ctx:上下文。
|
||||||
// - certPem:证书 PEM 内容。
|
// - certPEM:证书 PEM 内容。
|
||||||
// - privkeyPem:私钥 PEM 内容。
|
// - privkeyPEM:私钥 PEM 内容。
|
||||||
//
|
//
|
||||||
// 出参:
|
// 出参:
|
||||||
// - res:部署结果。
|
// - res:部署结果。
|
||||||
// - err: 错误。
|
// - err: 错误。
|
||||||
Deploy(ctx context.Context, certPem string, privkeyPem string) (res *DeployResult, err error)
|
Deploy(ctx context.Context, certPEM string, privkeyPEM string) (res *DeployResult, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表示证书部署结果的数据结构。
|
// 表示证书部署结果的数据结构。
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package onepanelconsole
|
package onepanelconsole
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||||
opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -26,7 +25,7 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *opsdk.Client
|
sdkClient *opsdk.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,26 +38,30 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 设置面板 SSL 证书
|
// 设置面板 SSL 证书
|
||||||
updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
|
updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
|
||||||
Cert: certPem,
|
Cert: certPEM,
|
||||||
Key: privkeyPem,
|
Key: privkeyPEM,
|
||||||
SSL: "enable",
|
SSL: "enable",
|
||||||
SSLType: "import-paste",
|
SSLType: "import-paste",
|
||||||
}
|
}
|
||||||
@@ -68,16 +71,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
updateSystemSSLReq.AutoRestart = "false"
|
updateSystemSSLReq.AutoRestart = "false"
|
||||||
}
|
}
|
||||||
updateSystemSSLResp, err := d.sdkClient.UpdateSystemSSL(updateSystemSSLReq)
|
updateSystemSSLResp, err := d.sdkClient.UpdateSystemSSL(updateSystemSSLReq)
|
||||||
|
d.logger.Debug("sdk request '1panel.UpdateSystemSSL'", slog.Any("request", updateSystemSSLReq), slog.Any("response", updateSystemSSLResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateSystemSSL'")
|
return nil, fmt.Errorf("failed to execute sdk request '1panel.UpdateSystemSSL': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已设置面板 SSL 证书", updateSystemSSLResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) {
|
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
|
||||||
if _, err := url.Parse(apiUrl); err != nil {
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
return nil, errors.New("invalid 1panel api url")
|
return nil, errors.New("invalid 1panel api url")
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := opsdk.NewClient(apiUrl, apiKey)
|
client := opsdk.NewClient(apiUrl, apiKey)
|
||||||
if allowInsecure {
|
if skipTlsVerify {
|
||||||
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package onepanelconsole_test
|
package onepanelconsole_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
package onepanelsite
|
package onepanelsite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
|
||||||
opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
|
opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -23,13 +22,19 @@ type DeployerConfig struct {
|
|||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
// 是否允许不安全的连接。
|
// 是否允许不安全的连接。
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
// 网站 ID。
|
// 网站 ID。
|
||||||
WebsiteId int64 `json:"websiteId"`
|
// 部署资源类型为 [RESOURCE_TYPE_WEBSITE] 时必填。
|
||||||
|
WebsiteId int64 `json:"websiteId,omitempty"`
|
||||||
|
// 证书 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||||
|
CertificateId int64 `json:"certificateId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *opsdk.Client
|
sdkClient *opsdk.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
@@ -43,7 +48,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
@@ -51,40 +56,68 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
ApiKey: config.ApiKey,
|
ApiKey: config.ApiKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case RESOURCE_TYPE_WEBSITE:
|
||||||
|
if err := d.deployToWebsite(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case RESOURCE_TYPE_CERTIFICATE:
|
||||||
|
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.WebsiteId == 0 {
|
||||||
|
return errors.New("config `websiteId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
// 获取网站 HTTPS 配置
|
// 获取网站 HTTPS 配置
|
||||||
getHttpsConfReq := &opsdk.GetHttpsConfRequest{
|
getHttpsConfReq := &opsdk.GetHttpsConfRequest{
|
||||||
WebsiteID: d.config.WebsiteId,
|
WebsiteID: d.config.WebsiteId,
|
||||||
}
|
}
|
||||||
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
|
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
|
||||||
|
d.logger.Debug("sdk request '1panel.GetHttpsConf'", slog.Any("request", getHttpsConfReq), slog.Any("response", getHttpsConfResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.GetHttpsConf'")
|
return fmt.Errorf("failed to execute sdk request '1panel.GetHttpsConf': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已获取网站 HTTPS 配置", getHttpsConfResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书到面板
|
// 上传证书到面板
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
} else {
|
} else {
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改网站 HTTPS 配置
|
// 修改网站 HTTPS 配置
|
||||||
@@ -100,16 +133,47 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
Hsts: getHttpsConfResp.Data.Hsts,
|
Hsts: getHttpsConfResp.Data.Hsts,
|
||||||
}
|
}
|
||||||
updateHttpsConfResp, err := d.sdkClient.UpdateHttpsConf(updateHttpsConfReq)
|
updateHttpsConfResp, err := d.sdkClient.UpdateHttpsConf(updateHttpsConfReq)
|
||||||
|
d.logger.Debug("sdk request '1panel.UpdateHttpsConf'", slog.Any("request", updateHttpsConfReq), slog.Any("response", updateHttpsConfResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateHttpsConf'")
|
return fmt.Errorf("failed to execute sdk request '1panel.UpdateHttpsConf': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已获取网站 HTTPS 配置", updateHttpsConfResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) {
|
func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.CertificateId == 0 {
|
||||||
|
return errors.New("config `certificateId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取证书详情
|
||||||
|
getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{
|
||||||
|
SSLID: d.config.CertificateId,
|
||||||
|
}
|
||||||
|
getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq)
|
||||||
|
d.logger.Debug("sdk request '1panel.GetWebsiteSSL'", slog.Any("request", getWebsiteSSLReq), slog.Any("response", getWebsiteSSLResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request '1panel.GetWebsiteSSL': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新证书
|
||||||
|
uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
|
||||||
|
Type: "paste",
|
||||||
|
SSLID: d.config.CertificateId,
|
||||||
|
Description: getWebsiteSSLResp.Data.Description,
|
||||||
|
Certificate: certPEM,
|
||||||
|
PrivateKey: privkeyPEM,
|
||||||
|
}
|
||||||
|
uploadWebsiteSSLResp, err := d.sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq)
|
||||||
|
d.logger.Debug("sdk request '1panel.UploadWebsiteSSL'", slog.Any("request", uploadWebsiteSSLReq), slog.Any("response", uploadWebsiteSSLResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request '1panel.UploadWebsiteSSL': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
|
||||||
if _, err := url.Parse(apiUrl); err != nil {
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
return nil, errors.New("invalid 1panel api url")
|
return nil, errors.New("invalid 1panel api url")
|
||||||
}
|
}
|
||||||
@@ -119,7 +183,7 @@ func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := opsdk.NewClient(apiUrl, apiKey)
|
client := opsdk.NewClient(apiUrl, apiKey)
|
||||||
if allowInsecure {
|
if skipTlsVerify {
|
||||||
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package onepanelsite_test
|
package onepanelsite_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -20,7 +20,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
|
argsPrefix := "CERTIMATE_DEPLOYER_1PANELSITE_"
|
||||||
|
|
||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
@@ -32,12 +32,12 @@ func init() {
|
|||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./1panel_console_test.go -args \
|
go test -v ./1panel_site_test.go -args \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_WEBSITEID="your-website-id"
|
--CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -55,8 +55,9 @@ func TestDeploy(t *testing.T) {
|
|||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
ApiUrl: fApiUrl,
|
ApiUrl: fApiUrl,
|
||||||
ApiKey: fApiKey,
|
ApiKey: fApiKey,
|
||||||
WebsiteId: fWebsiteId,
|
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
|
||||||
|
WebsiteId: fWebsiteId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
|||||||
10
internal/pkg/core/deployer/providers/1panel-site/consts.go
Normal file
10
internal/pkg/core/deployer/providers/1panel-site/consts.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package onepanelsite
|
||||||
|
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:替换指定网站的证书。
|
||||||
|
RESOURCE_TYPE_WEBSITE = ResourceType("website")
|
||||||
|
// 资源类型:替换指定证书。
|
||||||
|
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||||
|
)
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
package aliyunalb
|
package aliyunalb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunAlb "github.com/alibabacloud-go/alb-20200616/v2/client"
|
alialb "github.com/alibabacloud-go/alb-20200616/v2/client"
|
||||||
aliyunCas "github.com/alibabacloud-go/cas-20200407/v3/client"
|
alicas "github.com/alibabacloud-go/cas-20200407/v3/client"
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
)
|
)
|
||||||
@@ -43,7 +42,7 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClients *wSdkClients
|
sdkClients *wSdkClients
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
@@ -51,8 +50,8 @@ type DeployerProvider struct {
|
|||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
type wSdkClients struct {
|
type wSdkClients struct {
|
||||||
alb *aliyunAlb.Client
|
ALB *alialb.Client
|
||||||
cas *aliyunCas.Client
|
CAS *alicas.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
@@ -62,36 +61,41 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk clients")
|
return nil, fmt.Errorf("failed to create sdk clients: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClients: clients,
|
sdkClients: clients,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 上传证书到 CAS
|
// 上传证书到 CAS
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
|
||||||
|
|
||||||
// 根据部署资源类型决定部署方式
|
// 根据部署资源类型决定部署方式
|
||||||
switch d.config.ResourceType {
|
switch d.config.ResourceType {
|
||||||
case RESOURCE_TYPE_LOADBALANCER:
|
case RESOURCE_TYPE_LOADBALANCER:
|
||||||
@@ -105,7 +109,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
@@ -118,31 +122,37 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
// 查询负载均衡实例的详细信息
|
// 查询负载均衡实例的详细信息
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
|
||||||
getLoadBalancerAttributeReq := &aliyunAlb.GetLoadBalancerAttributeRequest{
|
getLoadBalancerAttributeReq := &alialb.GetLoadBalancerAttributeRequest{
|
||||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||||
}
|
}
|
||||||
getLoadBalancerAttributeResp, err := d.sdkClients.alb.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 ALB 负载均衡实例", getLoadBalancerAttributeResp)
|
|
||||||
|
|
||||||
// 查询 HTTPS 监听列表
|
// 查询 HTTPS 监听列表
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||||
listenerIds := make([]string, 0)
|
listenerIds := make([]string, 0)
|
||||||
listListenersLimit := int32(100)
|
listListenersLimit := int32(100)
|
||||||
var listListenersToken *string = nil
|
var listListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
listListenersReq := &aliyunAlb.ListListenersRequest{
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listListenersReq := &alialb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||||
ListenerProtocol: tea.String("HTTPS"),
|
ListenerProtocol: tea.String("HTTPS"),
|
||||||
}
|
}
|
||||||
listListenersResp, err := d.sdkClients.alb.ListListeners(listListenersReq)
|
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
|
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
@@ -158,21 +168,26 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", listenerIds)
|
|
||||||
|
|
||||||
// 查询 QUIC 监听列表
|
// 查询 QUIC 监听列表
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||||
listListenersToken = nil
|
listListenersToken = nil
|
||||||
for {
|
for {
|
||||||
listListenersReq := &aliyunAlb.ListListenersRequest{
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listListenersReq := &alialb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||||
ListenerProtocol: tea.String("QUIC"),
|
ListenerProtocol: tea.String("QUIC"),
|
||||||
}
|
}
|
||||||
listListenersResp, err := d.sdkClients.alb.ListListeners(listListenersReq)
|
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
|
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
@@ -188,13 +203,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", listenerIds)
|
|
||||||
|
|
||||||
// 遍历更新监听证书
|
// 遍历更新监听证书
|
||||||
if len(listenerIds) == 0 {
|
if len(listenerIds) == 0 {
|
||||||
return errors.New("listener not found")
|
d.logger.Info("no alb listeners to deploy")
|
||||||
} else {
|
} else {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
@@ -226,51 +240,56 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
|
|||||||
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
|
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
|
||||||
// 查询监听的属性
|
// 查询监听的属性
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
|
||||||
getListenerAttributeReq := &aliyunAlb.GetListenerAttributeRequest{
|
getListenerAttributeReq := &alialb.GetListenerAttributeRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
}
|
}
|
||||||
getListenerAttributeResp, err := d.sdkClients.alb.GetListenerAttribute(getListenerAttributeReq)
|
getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttribute(getListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 ALB 监听配置", getListenerAttributeResp)
|
|
||||||
|
|
||||||
if d.config.Domain == "" {
|
if d.config.Domain == "" {
|
||||||
// 未指定 SNI,只需部署到监听器
|
// 未指定 SNI,只需部署到监听器
|
||||||
|
|
||||||
// 修改监听的属性
|
// 修改监听的属性
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
|
||||||
updateListenerAttributeReq := &aliyunAlb.UpdateListenerAttributeRequest{
|
updateListenerAttributeReq := &alialb.UpdateListenerAttributeRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
Certificates: []*aliyunAlb.UpdateListenerAttributeRequestCertificates{{
|
Certificates: []*alialb.UpdateListenerAttributeRequestCertificates{{
|
||||||
CertificateId: tea.String(cloudCertId),
|
CertificateId: tea.String(cloudCertId),
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
updateListenerAttributeResp, err := d.sdkClients.alb.UpdateListenerAttribute(updateListenerAttributeReq)
|
updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已更新 ALB 监听配置", updateListenerAttributeResp)
|
|
||||||
} else {
|
} else {
|
||||||
// 指定 SNI,需部署到扩展域名
|
// 指定 SNI,需部署到扩展域名
|
||||||
|
|
||||||
// 查询监听证书列表
|
// 查询监听证书列表
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||||
listenerCertificates := make([]aliyunAlb.ListListenerCertificatesResponseBodyCertificates, 0)
|
listenerCertificates := make([]alialb.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||||
listListenerCertificatesLimit := int32(100)
|
listListenerCertificatesLimit := int32(100)
|
||||||
var listListenerCertificatesToken *string = nil
|
var listListenerCertificatesToken *string = nil
|
||||||
for {
|
for {
|
||||||
listListenerCertificatesReq := &aliyunAlb.ListListenerCertificatesRequest{
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
|
||||||
NextToken: listListenerCertificatesToken,
|
NextToken: listListenerCertificatesToken,
|
||||||
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
CertificateType: tea.String("Server"),
|
CertificateType: tea.String("Server"),
|
||||||
}
|
}
|
||||||
listListenerCertificatesResp, err := d.sdkClients.alb.ListListenerCertificates(listListenerCertificatesReq)
|
listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificates(listListenerCertificatesReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListenerCertificates'")
|
return fmt.Errorf("failed to execute sdk request 'alb.ListListenerCertificates': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if listListenerCertificatesResp.Body.Certificates != nil {
|
if listListenerCertificatesResp.Body.Certificates != nil {
|
||||||
@@ -286,53 +305,65 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 ALB 监听下全部证书", listenerCertificates)
|
|
||||||
|
|
||||||
// 遍历查询监听证书,并找出需要解除关联的证书
|
// 遍历查询监听证书,并找出需要解除关联的证书
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail
|
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail
|
||||||
certificateIsAssociated := false
|
certificateIsAlreadyAssociated := false
|
||||||
certificateIdsExpired := make([]string, 0)
|
certificateIdsToDissociate := make([]string, 0)
|
||||||
if len(listenerCertificates) > 0 {
|
if len(listenerCertificates) > 0 {
|
||||||
|
d.logger.Info("found listener certificates to deploy", slog.Any("listenerCertificates", listenerCertificates))
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerCertificate := range listenerCertificates {
|
for _, listenerCertificate := range listenerCertificates {
|
||||||
if *listenerCertificate.CertificateId == cloudCertId {
|
if tea.BoolValue(listenerCertificate.IsDefault) {
|
||||||
certificateIsAssociated = true
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if *listenerCertificate.IsDefault || !strings.EqualFold(*listenerCertificate.Status, "Associated") {
|
if !strings.EqualFold(tea.StringValue(listenerCertificate.Status), "Associated") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
listenerCertificateId, err := strconv.ParseInt(*listenerCertificate.CertificateId, 10, 64)
|
// 监听证书 ID 格式:${证书 ID}-${地域}
|
||||||
|
certificateId := strings.Split(tea.StringValue(listenerCertificate.CertificateId), "-")[0]
|
||||||
|
if certificateId == cloudCertId {
|
||||||
|
certificateIsAlreadyAssociated = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
certificateIdAsInt64, err := strconv.ParseInt(certificateId, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserCertificateDetailReq := &aliyunCas.GetUserCertificateDetailRequest{
|
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
|
||||||
CertId: tea.Int64(listenerCertificateId),
|
CertId: tea.Int64(certificateIdAsInt64),
|
||||||
}
|
}
|
||||||
getUserCertificateDetailResp, err := d.sdkClients.cas.GetUserCertificateDetail(getUserCertificateDetailReq)
|
getUserCertificateDetailResp, err := d.sdkClients.CAS.GetUserCertificateDetail(getUserCertificateDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'cas.GetUserCertificateDetail'"))
|
if sdkerr, ok := err.(*tea.SDKError); ok {
|
||||||
continue
|
if tea.IntValue(sdkerr.StatusCode) == 400 && tea.StringValue(sdkerr.Code) == "NotFound" {
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
certCnMatched := getUserCertificateDetailResp.Body.Common != nil && *getUserCertificateDetailResp.Body.Common == d.config.Domain
|
errs = append(errs, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err))
|
||||||
certSanMatched := getUserCertificateDetailResp.Body.Sans != nil && slices.Contains(strings.Split(*getUserCertificateDetailResp.Body.Sans, ","), d.config.Domain)
|
|
||||||
if !certCnMatched && !certSanMatched {
|
|
||||||
continue
|
continue
|
||||||
}
|
} else {
|
||||||
|
certCNMatched := tea.StringValue(getUserCertificateDetailResp.Body.Common) == d.config.Domain
|
||||||
|
certSANMatched := slices.Contains(strings.Split(tea.StringValue(getUserCertificateDetailResp.Body.Sans), ","), d.config.Domain)
|
||||||
|
if !certCNMatched && !certSANMatched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
certEndDate, _ := time.Parse("2006-01-02", *getUserCertificateDetailResp.Body.EndDate)
|
certEndDate, _ := time.Parse("2006-01-02", tea.StringValue(getUserCertificateDetailResp.Body.EndDate))
|
||||||
if time.Now().Before(certEndDate) {
|
if time.Now().Before(certEndDate) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
certificateIdsExpired = append(certificateIdsExpired, *listenerCertificate.CertificateId)
|
certificateIdsToDissociate = append(certificateIdsToDissociate, certificateId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
@@ -342,43 +373,41 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
|
|||||||
|
|
||||||
// 关联监听和扩展证书
|
// 关联监听和扩展证书
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-associateadditionalcertificateswithlistener
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-associateadditionalcertificateswithlistener
|
||||||
if !certificateIsAssociated {
|
if !certificateIsAlreadyAssociated {
|
||||||
associateAdditionalCertificatesFromListenerReq := &aliyunAlb.AssociateAdditionalCertificatesWithListenerRequest{
|
associateAdditionalCertificatesFromListenerReq := &alialb.AssociateAdditionalCertificatesWithListenerRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
Certificates: []*aliyunAlb.AssociateAdditionalCertificatesWithListenerRequestCertificates{
|
Certificates: []*alialb.AssociateAdditionalCertificatesWithListenerRequestCertificates{
|
||||||
{
|
{
|
||||||
CertificateId: tea.String(cloudCertId),
|
CertificateId: tea.String(cloudCertId),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.alb.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq)
|
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesFromListenerReq), slog.Any("response", associateAdditionalCertificatesFromListenerResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener'")
|
return fmt.Errorf("failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已关联 ALB 监听和扩展证书", associateAdditionalCertificatesFromListenerResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解除关联监听和扩展证书
|
// 解除关联监听和扩展证书
|
||||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-dissociateadditionalcertificatesfromlistener
|
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-dissociateadditionalcertificatesfromlistener
|
||||||
if len(certificateIdsExpired) > 0 {
|
if !certificateIsAlreadyAssociated && len(certificateIdsToDissociate) > 0 {
|
||||||
dissociateAdditionalCertificates := make([]*aliyunAlb.DissociateAdditionalCertificatesFromListenerRequestCertificates, 0)
|
dissociateAdditionalCertificates := make([]*alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates, 0)
|
||||||
for _, certificateId := range certificateIdsExpired {
|
for _, certificateId := range certificateIdsToDissociate {
|
||||||
dissociateAdditionalCertificates = append(dissociateAdditionalCertificates, &aliyunAlb.DissociateAdditionalCertificatesFromListenerRequestCertificates{
|
dissociateAdditionalCertificates = append(dissociateAdditionalCertificates, &alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates{
|
||||||
CertificateId: tea.String(certificateId),
|
CertificateId: tea.String(certificateId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
dissociateAdditionalCertificatesFromListenerReq := &aliyunAlb.DissociateAdditionalCertificatesFromListenerRequest{
|
dissociateAdditionalCertificatesFromListenerReq := &alialb.DissociateAdditionalCertificatesFromListenerRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
Certificates: dissociateAdditionalCertificates,
|
Certificates: dissociateAdditionalCertificates,
|
||||||
}
|
}
|
||||||
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.alb.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq)
|
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq)
|
||||||
|
d.logger.Debug("sdk request 'alb.DissociateAdditionalCertificatesFromListener'", slog.Any("request", dissociateAdditionalCertificatesFromListenerReq), slog.Any("response", dissociateAdditionalCertificatesFromListenerResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener'")
|
return fmt.Errorf("failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已解除关联 ALB 监听和扩展证书", dissociateAdditionalCertificatesFromListenerResp)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,12 +424,12 @@ func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients
|
|||||||
albEndpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
|
albEndpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
albConfig := &aliyunOpen.Config{
|
albConfig := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(albEndpoint),
|
Endpoint: tea.String(albEndpoint),
|
||||||
}
|
}
|
||||||
albClient, err := aliyunAlb.NewClient(albConfig)
|
albClient, err := alialb.NewClient(albConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -413,19 +442,19 @@ func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients
|
|||||||
casEndpoint = "cas.aliyuncs.com"
|
casEndpoint = "cas.aliyuncs.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
casConfig := &aliyunOpen.Config{
|
casConfig := &aliopen.Config{
|
||||||
Endpoint: tea.String(casEndpoint),
|
Endpoint: tea.String(casEndpoint),
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
}
|
}
|
||||||
casClient, err := aliyunCas.NewClient(casConfig)
|
casClient, err := alicas.NewClient(casConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &wSdkClients{
|
return &wSdkClients{
|
||||||
alb: albClient,
|
ALB: albClient,
|
||||||
cas: casClient,
|
CAS: casClient,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +464,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
|
|||||||
// 阿里云 CAS 服务接入点是独立于 ALB 服务的
|
// 阿里云 CAS 服务接入点是独立于 ALB 服务的
|
||||||
// 国内版固定接入点:华东一杭州
|
// 国内版固定接入点:华东一杭州
|
||||||
// 国际版固定接入点:亚太东南一新加坡
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
casRegion = "ap-southeast-1"
|
casRegion = "ap-southeast-1"
|
||||||
} else {
|
} else {
|
||||||
casRegion = "cn-hangzhou"
|
casRegion = "cn-hangzhou"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunalb_test
|
package aliyunalb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunalb
|
package aliyunalb
|
||||||
|
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package aliyunapigw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
aliapig "github.com/alibabacloud-go/apig-20240327/v3/client"
|
||||||
|
alicloudapi "github.com/alibabacloud-go/cloudapi-20160714/v5/client"
|
||||||
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 服务类型。
|
||||||
|
ServiceType ServiceType `json:"serviceType"`
|
||||||
|
// API 网关 ID。
|
||||||
|
// 服务类型为 [SERVICE_TYPE_CLOUDNATIVE] 时必填。
|
||||||
|
GatewayId string `json:"gatewayId,omitempty"`
|
||||||
|
// API 分组 ID。
|
||||||
|
// 服务类型为 [SERVICE_TYPE_TRADITIONAL] 时必填。
|
||||||
|
GroupId string `json:"groupId,omitempty"`
|
||||||
|
// 自定义域名(支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClients *wSdkClients
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
type wSdkClients struct {
|
||||||
|
CloudNativeAPIGateway *aliapig.Client
|
||||||
|
TraditionalAPIGateway *alicloudapi.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk clients: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClients: clients,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
switch d.config.ServiceType {
|
||||||
|
case SERVICE_TYPE_TRADITIONAL:
|
||||||
|
if err := d.deployToTraditional(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case SERVICE_TYPE_CLOUDNATIVE:
|
||||||
|
if err := d.deployToCloudNative(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported service type '%s'", string(d.config.ServiceType))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToTraditional(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.GroupId == "" {
|
||||||
|
return errors.New("config `groupId` is required")
|
||||||
|
}
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
return errors.New("config `domain` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为自定义域名添加 SSL 证书
|
||||||
|
// REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate
|
||||||
|
setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{
|
||||||
|
GroupId: tea.String(d.config.GroupId),
|
||||||
|
DomainName: tea.String(d.config.Domain),
|
||||||
|
CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
|
||||||
|
CertificateBody: tea.String(certPEM),
|
||||||
|
CertificatePrivateKey: tea.String(privkeyPEM),
|
||||||
|
}
|
||||||
|
setDomainCertificateResp, err := d.sdkClients.TraditionalAPIGateway.SetDomainCertificate(setDomainCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'apigateway.SetDomainCertificate'", slog.Any("request", setDomainCertificateReq), slog.Any("response", setDomainCertificateResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'apigateway.SetDomainCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToCloudNative(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.GatewayId == "" {
|
||||||
|
return errors.New("config `gatewayId` is required")
|
||||||
|
}
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
return errors.New("config `domain` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历查询域名列表,获取域名 ID
|
||||||
|
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains
|
||||||
|
var domainId string
|
||||||
|
listDomainsPageNumber := int32(1)
|
||||||
|
listDomainsPageSize := int32(10)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||||
|
GatewayId: tea.String(d.config.GatewayId),
|
||||||
|
NameLike: tea.String(d.config.Domain),
|
||||||
|
PageNumber: tea.Int32(listDomainsPageNumber),
|
||||||
|
PageSize: tea.Int32(listDomainsPageSize),
|
||||||
|
}
|
||||||
|
listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomains(listDomainsReq)
|
||||||
|
d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listDomainsResp.Body.Data.Items != nil {
|
||||||
|
for _, domainInfo := range listDomainsResp.Body.Data.Items {
|
||||||
|
if strings.EqualFold(tea.StringValue(domainInfo.Name), d.config.Domain) {
|
||||||
|
domainId = tea.StringValue(domainInfo.DomainId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if domainId != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listDomainsResp.Body.Data.Items == nil || len(listDomainsResp.Body.Data.Items) < int(listDomainsPageSize) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listDomainsPageNumber++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if domainId == "" {
|
||||||
|
return errors.New("domain not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询域名
|
||||||
|
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain
|
||||||
|
getDomainReq := &aliapig.GetDomainRequest{}
|
||||||
|
getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomain(tea.String(domainId), getDomainReq)
|
||||||
|
d.logger.Debug("sdk request 'apig.GetDomain'", slog.Any("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新域名
|
||||||
|
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain
|
||||||
|
updateDomainReq := &aliapig.UpdateDomainRequest{
|
||||||
|
Protocol: tea.String("HTTPS"),
|
||||||
|
ForceHttps: getDomainResp.Body.Data.ForceHttps,
|
||||||
|
MTLSEnabled: getDomainResp.Body.Data.MTLSEnabled,
|
||||||
|
Http2Option: getDomainResp.Body.Data.Http2Option,
|
||||||
|
TlsMin: getDomainResp.Body.Data.TlsMin,
|
||||||
|
TlsMax: getDomainResp.Body.Data.TlsMax,
|
||||||
|
TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig,
|
||||||
|
CertIdentifier: tea.String(upres.ExtendedData["certIdentifier"].(string)),
|
||||||
|
}
|
||||||
|
updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomain(tea.String(domainId), updateDomainReq)
|
||||||
|
d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.Any("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients, error) {
|
||||||
|
// 接入点一览 https://api.aliyun.com/product/APIG
|
||||||
|
cloudNativeAPIGEndpoint := fmt.Sprintf("apig.%s.aliyuncs.com", region)
|
||||||
|
cloudNativeAPIGConfig := &aliopen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(cloudNativeAPIGEndpoint),
|
||||||
|
}
|
||||||
|
cloudNativeAPIGClient, err := aliapig.NewClient(cloudNativeAPIGConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接入点一览 https://api.aliyun.com/product/CloudAPI
|
||||||
|
traditionalAPIGEndpoint := fmt.Sprintf("apigateway.%s.aliyuncs.com", region)
|
||||||
|
traditionalAPIGConfig := &aliopen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(traditionalAPIGEndpoint),
|
||||||
|
}
|
||||||
|
traditionalAPIGClient, err := alicloudapi.NewClient(traditionalAPIGConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wSdkClients{
|
||||||
|
CloudNativeAPIGateway: cloudNativeAPIGClient,
|
||||||
|
TraditionalAPIGateway: traditionalAPIGClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) {
|
||||||
|
casRegion := region
|
||||||
|
if casRegion != "" {
|
||||||
|
// 阿里云 CAS 服务接入点是独立于 APIGateway 服务的
|
||||||
|
// 国内版固定接入点:华东一杭州
|
||||||
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
|
casRegion = "ap-southeast-1"
|
||||||
|
} else {
|
||||||
|
casRegion = "cn-hangzhou"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: accessKeyId,
|
||||||
|
AccessKeySecret: accessKeySecret,
|
||||||
|
Region: casRegion,
|
||||||
|
})
|
||||||
|
return uploader, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package aliyunapigw_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-apigw"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fServiceType string
|
||||||
|
fGatewayId string
|
||||||
|
fGroupId string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNAPIGW_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fGatewayId, argsPrefix+"GATEWARYID", "", "")
|
||||||
|
flag.StringVar(&fGroupId, argsPrefix+"GROUPID", "", "")
|
||||||
|
flag.StringVar(&fServiceType, argsPrefix+"SERVICETYPE", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./aliyun_apigw_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_GATEWAYID="your-api-gateway-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_GROUPID="your-api-group-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_SERVICETYPE="cloudnative" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNAPIGW_DOMAIN="example.com"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("GATEWAYID: %v", fGatewayId),
|
||||||
|
fmt.Sprintf("GROUPID: %v", fGroupId),
|
||||||
|
fmt.Sprintf("SERVICETYPE: %v", fServiceType),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
ServiceType: provider.ServiceType(fServiceType),
|
||||||
|
GatewayId: fGatewayId,
|
||||||
|
GroupId: fGroupId,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
||||||
10
internal/pkg/core/deployer/providers/aliyun-apigw/consts.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-apigw/consts.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package aliyunapigw
|
||||||
|
|
||||||
|
type ServiceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 服务类型:原 API 网关。
|
||||||
|
SERVICE_TYPE_TRADITIONAL = ServiceType("traditional")
|
||||||
|
// 服务类型:云原生 API 网关。
|
||||||
|
SERVICE_TYPE_CLOUDNATIVE = ServiceType("cloudnative")
|
||||||
|
)
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
package aliyuncasdeploy
|
package aliyuncasdeploy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunCas "github.com/alibabacloud-go/cas-20200407/v3/client"
|
alicas "github.com/alibabacloud-go/cas-20200407/v3/client"
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
)
|
)
|
||||||
@@ -34,8 +33,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunCas.Client
|
sdkClient *alicas.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,50 +47,60 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
Region: config.Region,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
if len(d.config.ResourceIds) == 0 {
|
if len(d.config.ResourceIds) == 0 {
|
||||||
return nil, errors.New("config `resourceIds` is required")
|
return nil, errors.New("config `resourceIds` is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书到 CAS
|
// 上传证书到 CAS
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
|
||||||
|
|
||||||
contactIds := d.config.ContactIds
|
contactIds := d.config.ContactIds
|
||||||
if len(contactIds) == 0 {
|
if len(contactIds) == 0 {
|
||||||
// 获取联系人列表
|
// 获取联系人列表
|
||||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-listcontact
|
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-listcontact
|
||||||
listContactReq := &aliyunCas.ListContactRequest{}
|
listContactReq := &alicas.ListContactRequest{}
|
||||||
listContactReq.ShowSize = tea.Int32(1)
|
listContactReq.ShowSize = tea.Int32(1)
|
||||||
listContactReq.CurrentPage = tea.Int32(1)
|
listContactReq.CurrentPage = tea.Int32(1)
|
||||||
listContactResp, err := d.sdkClient.ListContact(listContactReq)
|
listContactResp, err := d.sdkClient.ListContact(listContactReq)
|
||||||
|
d.logger.Debug("sdk request 'cas.ListContact'", slog.Any("request", listContactReq), slog.Any("response", listContactResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.ListContact'")
|
return nil, fmt.Errorf("failed to execute sdk request 'cas.ListContact': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(listContactResp.Body.ContactList) > 0 {
|
if len(listContactResp.Body.ContactList) > 0 {
|
||||||
@@ -101,7 +110,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
|
|
||||||
// 创建部署任务
|
// 创建部署任务
|
||||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-createdeploymentjob
|
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-createdeploymentjob
|
||||||
createDeploymentJobReq := &aliyunCas.CreateDeploymentJobRequest{
|
createDeploymentJobReq := &alicas.CreateDeploymentJobRequest{
|
||||||
Name: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
Name: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
JobType: tea.String("user"),
|
JobType: tea.String("user"),
|
||||||
CertIds: tea.String(upres.CertId),
|
CertIds: tea.String(upres.CertId),
|
||||||
@@ -109,44 +118,45 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
ContactIds: tea.String(strings.Join(contactIds, ",")),
|
ContactIds: tea.String(strings.Join(contactIds, ",")),
|
||||||
}
|
}
|
||||||
createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJob(createDeploymentJobReq)
|
createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJob(createDeploymentJobReq)
|
||||||
|
d.logger.Debug("sdk request 'cas.CreateDeploymentJob'", slog.Any("request", createDeploymentJobReq), slog.Any("response", createDeploymentJobResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.CreateDeploymentJob'")
|
return nil, fmt.Errorf("failed to execute sdk request 'cas.CreateDeploymentJob': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已创建部署任务", createDeploymentJobResp)
|
|
||||||
|
|
||||||
// 循环获取部署任务详情,等待任务状态变更
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
|
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
|
||||||
for {
|
for {
|
||||||
if ctx.Err() != nil {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
describeDeploymentJobReq := &aliyunCas.DescribeDeploymentJobRequest{
|
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
|
||||||
JobId: createDeploymentJobResp.Body.JobId,
|
JobId: createDeploymentJobResp.Body.JobId,
|
||||||
}
|
}
|
||||||
describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJob(describeDeploymentJobReq)
|
describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJob(describeDeploymentJobReq)
|
||||||
|
d.logger.Debug("sdk request 'cas.DescribeDeploymentJob'", slog.Any("request", describeDeploymentJobReq), slog.Any("response", describeDeploymentJobResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cas.DescribeDeploymentJob'")
|
return nil, fmt.Errorf("failed to execute sdk request 'cas.DescribeDeploymentJob': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if describeDeploymentJobResp.Body.Status == nil || *describeDeploymentJobResp.Body.Status == "editing" {
|
if describeDeploymentJobResp.Body.Status == nil || *describeDeploymentJobResp.Body.Status == "editing" {
|
||||||
return nil, errors.New("部署任务状态异常")
|
return nil, errors.New("unexpected deployment job status")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *describeDeploymentJobResp.Body.Status == "success" || *describeDeploymentJobResp.Body.Status == "error" {
|
if *describeDeploymentJobResp.Body.Status == "success" || *describeDeploymentJobResp.Body.Status == "error" {
|
||||||
d.logger.Logt("已获取部署任务详情", describeDeploymentJobResp)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("部署任务未完成 ...")
|
d.logger.Info("waiting for deployment job completion ...")
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alicas.Client, error) {
|
||||||
if region == "" {
|
if region == "" {
|
||||||
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
|
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
|
||||||
}
|
}
|
||||||
@@ -160,25 +170,16 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Cl
|
|||||||
endpoint = fmt.Sprintf("cas.%s.aliyuncs.com", region)
|
endpoint = fmt.Sprintf("cas.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(endpoint),
|
Endpoint: tea.String(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunCas.NewClient(config)
|
client, err := alicas.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) {
|
|
||||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
|
||||||
AccessKeyId: accessKeyId,
|
|
||||||
AccessKeySecret: accessKeySecret,
|
|
||||||
Region: region,
|
|
||||||
})
|
|
||||||
return uploader, err
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package aliyuncas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
Region: config.Region,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
package aliyuncdn
|
package aliyuncdn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunCdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
|
alicdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -26,8 +25,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunCdn.Client
|
sdkClient *alicdn.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
@@ -39,53 +38,56 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式
|
// "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式
|
||||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||||
|
|
||||||
// 设置 CDN 域名域名证书
|
// 设置 CDN 域名域名证书
|
||||||
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
|
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
|
||||||
setCdnDomainSSLCertificateReq := &aliyunCdn.SetCdnDomainSSLCertificateRequest{
|
setCdnDomainSSLCertificateReq := &alicdn.SetCdnDomainSSLCertificateRequest{
|
||||||
DomainName: tea.String(domain),
|
DomainName: tea.String(domain),
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
SSLPub: tea.String(certPem),
|
SSLPub: tea.String(certPEM),
|
||||||
SSLPri: tea.String(privkeyPem),
|
SSLPri: tea.String(privkeyPEM),
|
||||||
}
|
}
|
||||||
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
|
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'cdn.SetCdnDomainSSLCertificate'", slog.Any("request", setCdnDomainSSLCertificateReq), slog.Any("response", setCdnDomainSSLCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'cdn.SetCdnDomainSSLCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已设置 CDN 域名证书", setCdnDomainSSLCertificateResp)
|
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunCdn.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret string) (*alicdn.Client, error) {
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String("cdn.aliyuncs.com"),
|
Endpoint: tea.String("cdn.aliyuncs.com"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunCdn.NewClient(config)
|
client, err := alicdn.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyuncdn_test
|
package aliyuncdn_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package aliyunclb
|
package aliyunclb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunSlb "github.com/alibabacloud-go/slb-20140515/v4/client"
|
alislb "github.com/alibabacloud-go/slb-20140515/v4/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
|
||||||
)
|
)
|
||||||
@@ -38,8 +37,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunSlb.Client
|
sdkClient *alislb.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,40 +51,41 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
AccessKeyId: config.AccessKeyId,
|
|
||||||
AccessKeySecret: config.AccessKeySecret,
|
|
||||||
Region: config.Region,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 上传证书到 SLB
|
// 上传证书到 SLB
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
|
||||||
|
|
||||||
// 根据部署资源类型决定部署方式
|
// 根据部署资源类型决定部署方式
|
||||||
switch d.config.ResourceType {
|
switch d.config.ResourceType {
|
||||||
case RESOURCE_TYPE_LOADBALANCER:
|
case RESOURCE_TYPE_LOADBALANCER:
|
||||||
@@ -99,7 +99,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
@@ -112,24 +112,29 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
// 查询负载均衡实例的详细信息
|
// 查询负载均衡实例的详细信息
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute
|
||||||
describeLoadBalancerAttributeReq := &aliyunSlb.DescribeLoadBalancerAttributeRequest{
|
describeLoadBalancerAttributeReq := &alislb.DescribeLoadBalancerAttributeRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||||
}
|
}
|
||||||
describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
|
describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerAttribute'", slog.Any("request", describeLoadBalancerAttributeReq), slog.Any("response", describeLoadBalancerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 CLB 负载均衡实例", describeLoadBalancerAttributeResp)
|
|
||||||
|
|
||||||
// 查询 HTTPS 监听列表
|
// 查询 HTTPS 监听列表
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
|
||||||
listenerPorts := make([]int32, 0)
|
listenerPorts := make([]int32, 0)
|
||||||
describeLoadBalancerListenersLimit := int32(100)
|
describeLoadBalancerListenersLimit := int32(100)
|
||||||
var describeLoadBalancerListenersToken *string = nil
|
var describeLoadBalancerListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
describeLoadBalancerListenersReq := &aliyunSlb.DescribeLoadBalancerListenersRequest{
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
|
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
|
||||||
NextToken: describeLoadBalancerListenersToken,
|
NextToken: describeLoadBalancerListenersToken,
|
||||||
@@ -137,8 +142,9 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
ListenerProtocol: tea.String("https"),
|
ListenerProtocol: tea.String("https"),
|
||||||
}
|
}
|
||||||
describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
|
describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerListeners'", slog.Any("request", describeLoadBalancerListenersReq), slog.Any("response", describeLoadBalancerListenersResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerListeners'")
|
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerListeners': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if describeLoadBalancerListenersResp.Body.Listeners != nil {
|
if describeLoadBalancerListenersResp.Body.Listeners != nil {
|
||||||
@@ -154,17 +160,22 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", listenerPorts)
|
|
||||||
|
|
||||||
// 遍历更新监听证书
|
// 遍历更新监听证书
|
||||||
if len(listenerPorts) == 0 {
|
if len(listenerPorts) == 0 {
|
||||||
return errors.New("listener not found")
|
d.logger.Info("no clb listeners to deploy")
|
||||||
} else {
|
} else {
|
||||||
|
d.logger.Info("found https listeners to deploy", slog.Any("listenerPorts", listenerPorts))
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerPort := range listenerPorts {
|
for _, listenerPort := range listenerPorts {
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,51 +206,48 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
|
|||||||
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId string) error {
|
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId string) error {
|
||||||
// 查询监听配置
|
// 查询监听配置
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute
|
||||||
describeLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.DescribeLoadBalancerHTTPSListenerAttributeRequest{
|
describeLoadBalancerHTTPSListenerAttributeReq := &alislb.DescribeLoadBalancerHTTPSListenerAttributeRequest{
|
||||||
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
||||||
ListenerPort: tea.Int32(cloudListenerPort),
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
}
|
}
|
||||||
describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
|
describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'", slog.Any("request", describeLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", describeLoadBalancerHTTPSListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 CLB HTTPS 监听配置", describeLoadBalancerHTTPSListenerAttributeResp)
|
|
||||||
|
|
||||||
if d.config.Domain == "" {
|
if d.config.Domain == "" {
|
||||||
// 未指定 SNI,只需部署到监听器
|
// 未指定 SNI,只需部署到监听器
|
||||||
|
|
||||||
// 修改监听配置
|
// 修改监听配置
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute
|
||||||
setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{
|
setLoadBalancerHTTPSListenerAttributeReq := &alislb.SetLoadBalancerHTTPSListenerAttributeRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
||||||
ListenerPort: tea.Int32(cloudListenerPort),
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
ServerCertificateId: tea.String(cloudCertId),
|
ServerCertificateId: tea.String(cloudCertId),
|
||||||
}
|
}
|
||||||
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
|
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp)
|
|
||||||
} else {
|
} else {
|
||||||
// 指定 SNI,需部署到扩展域名
|
// 指定 SNI,需部署到扩展域名
|
||||||
|
|
||||||
// 查询扩展域名
|
// 查询扩展域名
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
|
||||||
describeDomainExtensionsReq := &aliyunSlb.DescribeDomainExtensionsRequest{
|
describeDomainExtensionsReq := &alislb.DescribeDomainExtensionsRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
LoadBalancerId: tea.String(cloudLoadbalancerId),
|
||||||
ListenerPort: tea.Int32(cloudListenerPort),
|
ListenerPort: tea.Int32(cloudListenerPort),
|
||||||
}
|
}
|
||||||
describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
|
describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.DescribeDomainExtensions'", slog.Any("request", describeDomainExtensionsReq), slog.Any("response", describeDomainExtensionsResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'slb.DescribeDomainExtensions'")
|
return fmt.Errorf("failed to execute sdk request 'slb.DescribeDomainExtensions': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 CLB 扩展域名", describeDomainExtensionsResp)
|
|
||||||
|
|
||||||
// 遍历修改扩展域名
|
// 遍历修改扩展域名
|
||||||
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute
|
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute
|
||||||
if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil {
|
if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil {
|
||||||
@@ -250,18 +258,17 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
setDomainExtensionAttributeReq := &aliyunSlb.SetDomainExtensionAttributeRequest{
|
setDomainExtensionAttributeReq := &alislb.SetDomainExtensionAttributeRequest{
|
||||||
RegionId: tea.String(d.config.Region),
|
RegionId: tea.String(d.config.Region),
|
||||||
DomainExtensionId: tea.String(*domainExtension.DomainExtensionId),
|
DomainExtensionId: tea.String(*domainExtension.DomainExtensionId),
|
||||||
ServerCertificateId: tea.String(cloudCertId),
|
ServerCertificateId: tea.String(cloudCertId),
|
||||||
}
|
}
|
||||||
setDomainExtensionAttributeResp, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq)
|
setDomainExtensionAttributeResp, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'slb.SetDomainExtensionAttribute'", slog.Any("request", setDomainExtensionAttributeReq), slog.Any("response", setDomainExtensionAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'slb.SetDomainExtensionAttribute'"))
|
errs = append(errs, fmt.Errorf("failed to execute sdk request 'slb.SetDomainExtensionAttribute': %w", err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已修改 CLB 扩展域名", setDomainExtensionAttributeResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
@@ -273,7 +280,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alislb.Client, error) {
|
||||||
// 接入点一览 https://api.aliyun.com/product/Slb
|
// 接入点一览 https://api.aliyun.com/product/Slb
|
||||||
var endpoint string
|
var endpoint string
|
||||||
switch region {
|
switch region {
|
||||||
@@ -287,16 +294,25 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Cl
|
|||||||
endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
|
endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(endpoint),
|
Endpoint: tea.String(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunSlb.NewClient(config)
|
client, err := alislb.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) {
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: accessKeyId,
|
||||||
|
AccessKeySecret: accessKeySecret,
|
||||||
|
Region: region,
|
||||||
|
})
|
||||||
|
return uploader, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunclb_test
|
package aliyunclb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -46,7 +46,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_DEPLOYER_ALIYUNCLB_REGION="cn-hangzhou" \
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_REGION="cn-hangzhou" \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNCLB_LOADBALANCERID="your-clb-instance-id" \
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_LOADBALANCERID="your-clb-instance-id" \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNCLB_LISTENERPORT=443 \
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_LISTENERPORT=443 \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNCLB_DOMAIN="your-alb-sni-domain"
|
--CERTIMATE_DEPLOYER_ALIYUNCLB_DOMAIN="your-clb-sni-domain"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunclb
|
package aliyunclb
|
||||||
|
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package aliyundcdn
|
package aliyundcdn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunDcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
|
alidcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -26,8 +25,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunDcdn.Client
|
sdkClient *alidcdn.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
@@ -39,53 +38,56 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
||||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||||
|
|
||||||
// 配置域名证书
|
// 配置域名证书
|
||||||
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
|
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
|
||||||
setDcdnDomainSSLCertificateReq := &aliyunDcdn.SetDcdnDomainSSLCertificateRequest{
|
setDcdnDomainSSLCertificateReq := &alidcdn.SetDcdnDomainSSLCertificateRequest{
|
||||||
DomainName: tea.String(domain),
|
DomainName: tea.String(domain),
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
SSLPub: tea.String(certPem),
|
SSLPub: tea.String(certPEM),
|
||||||
SSLPri: tea.String(privkeyPem),
|
SSLPri: tea.String(privkeyPEM),
|
||||||
}
|
}
|
||||||
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
|
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'dcdn.SetDcdnDomainSSLCertificate'", slog.Any("request", setDcdnDomainSSLCertificateReq), slog.Any("response", setDcdnDomainSSLCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'dcdn.SetDcdnDomainSSLCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已配置 DCDN 域名证书", setDcdnDomainSSLCertificateResp)
|
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunDcdn.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret string) (*alidcdn.Client, error) {
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String("dcdn.aliyuncs.com"),
|
Endpoint: tea.String("dcdn.aliyuncs.com"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunDcdn.NewClient(config)
|
client, err := alidcdn.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyundcdn_test
|
package aliyundcdn_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
137
internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos.go
Normal file
137
internal/pkg/core/deployer/providers/aliyun-ddos/aliyun_ddos.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package aliyunddos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
aliddos "github.com/alibabacloud-go/ddoscoo-20200101/v4/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 阿里云地域。
|
||||||
|
Region string `json:"region"`
|
||||||
|
// 网站域名(支持泛域名)。
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *aliddos.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
return nil, errors.New("config `domain` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为网站业务转发规则关联 SSL 证书
|
||||||
|
// REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-associatewebcert
|
||||||
|
certId, _ := strconv.Atoi(upres.CertId)
|
||||||
|
associateWebCertReq := &aliddos.AssociateWebCertRequest{
|
||||||
|
Domain: tea.String(d.config.Domain),
|
||||||
|
CertId: tea.Int32(int32(certId)),
|
||||||
|
}
|
||||||
|
associateWebCertResp, err := d.sdkClient.AssociateWebCert(associateWebCertReq)
|
||||||
|
d.logger.Debug("sdk request 'dcdn.AssociateWebCert'", slog.Any("request", associateWebCertReq), slog.Any("response", associateWebCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'dcdn.AssociateWebCert': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliddos.Client, error) {
|
||||||
|
// 接入点一览 https://api.aliyun.com/product/ddoscoo
|
||||||
|
config := &aliopen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String(fmt.Sprintf("ddoscoo.%s.aliyuncs.com", region)),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliddos.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Uploader, error) {
|
||||||
|
casRegion := region
|
||||||
|
if casRegion != "" {
|
||||||
|
// 阿里云 CAS 服务接入点是独立于 Anti-DDoS 服务的
|
||||||
|
// 国内版固定接入点:华东一杭州
|
||||||
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
|
casRegion = "ap-southeast-1"
|
||||||
|
} else {
|
||||||
|
casRegion = "cn-hangzhou"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: accessKeyId,
|
||||||
|
AccessKeySecret: accessKeySecret,
|
||||||
|
Region: casRegion,
|
||||||
|
})
|
||||||
|
return uploader, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package aliyunddos_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ddos"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fRegion string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNDDOS_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./aliyun_ddos_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_REGION="cn-hangzhou" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNDDOS_DOMAIN="example.com"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Region: fRegion,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
package aliyunesa
|
package aliyunesa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunEsa "github.com/alibabacloud-go/esa-20240910/v2/client"
|
aliesa "github.com/alibabacloud-go/esa-20240910/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
)
|
)
|
||||||
@@ -31,8 +30,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunEsa.Client
|
sdkClient *aliesa.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,67 +44,71 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
if d.config.SiteId == 0 {
|
if d.config.SiteId == 0 {
|
||||||
return nil, errors.New("config `siteId` is required")
|
return nil, errors.New("config `siteId` is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书到 CAS
|
// 上传证书到 CAS
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
|
||||||
|
|
||||||
// 配置站点证书
|
// 配置站点证书
|
||||||
// REF: https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-setcertificate
|
// REF: https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-setcertificate
|
||||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||||
setCertificateReq := &aliyunEsa.SetCertificateRequest{
|
setCertificateReq := &aliesa.SetCertificateRequest{
|
||||||
SiteId: tea.Int64(d.config.SiteId),
|
SiteId: tea.Int64(d.config.SiteId),
|
||||||
Type: tea.String("cas"),
|
Type: tea.String("cas"),
|
||||||
CasId: tea.Int64(certId),
|
CasId: tea.Int64(certId),
|
||||||
}
|
}
|
||||||
setCertificateResp, err := d.sdkClient.SetCertificate(setCertificateReq)
|
setCertificateResp, err := d.sdkClient.SetCertificate(setCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'esa.SetCertificate'", slog.Any("request", setCertificateReq), slog.Any("response", setCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'esa.SetCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'esa.SetCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已配置站点证书", setCertificateResp)
|
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunEsa.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliesa.Client, error) {
|
||||||
// 接入点一览 https://api.aliyun.com/product/ESA
|
// 接入点一览 https://api.aliyun.com/product/ESA
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", region)),
|
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", region)),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunEsa.NewClient(config)
|
client, err := aliesa.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -119,7 +122,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
|
|||||||
// 阿里云 CAS 服务接入点是独立于 ESA 服务的
|
// 阿里云 CAS 服务接入点是独立于 ESA 服务的
|
||||||
// 国内版固定接入点:华东一杭州
|
// 国内版固定接入点:华东一杭州
|
||||||
// 国际版固定接入点:亚太东南一新加坡
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
casRegion = "ap-southeast-1"
|
casRegion = "ap-southeast-1"
|
||||||
} else {
|
} else {
|
||||||
casRegion = "cn-hangzhou"
|
casRegion = "cn-hangzhou"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunesa_test
|
package aliyunesa_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
flag.Int64Var(&fSiteId, argsPrefix+"SITEID", "", "")
|
flag.Int64Var(&fSiteId, argsPrefix+"SITEID", 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package aliyunfc
|
package aliyunfc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunFc3 "github.com/alibabacloud-go/fc-20230330/v4/client"
|
alifc3 "github.com/alibabacloud-go/fc-20230330/v4/client"
|
||||||
aliyunFc2 "github.com/alibabacloud-go/fc-open-20210406/v2/client"
|
alifc2 "github.com/alibabacloud-go/fc-open-20210406/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -23,23 +22,22 @@ type DeployerConfig struct {
|
|||||||
// 阿里云地域。
|
// 阿里云地域。
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
// 服务版本。
|
// 服务版本。
|
||||||
// 零值时默认为 "3.0"。
|
|
||||||
ServiceVersion string `json:"serviceVersion"`
|
ServiceVersion string `json:"serviceVersion"`
|
||||||
// 自定义域名(不支持泛域名)。
|
// 自定义域名(支持泛域名)。
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClients *wSdkClients
|
sdkClients *wSdkClients
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
type wSdkClients struct {
|
type wSdkClients struct {
|
||||||
fc2 *aliyunFc2.Client
|
FC2 *alifc2.Client
|
||||||
fc3 *aliyunFc3.Client
|
FC3 *alifc3.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
@@ -49,99 +47,99 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk clients")
|
return nil, fmt.Errorf("failed to create sdk clients: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClients: clients,
|
sdkClients: clients,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
switch d.config.ServiceVersion {
|
switch d.config.ServiceVersion {
|
||||||
case "", "3.0":
|
case "3", "3.0":
|
||||||
if err := d.deployToFC3(ctx, certPem, privkeyPem); err != nil {
|
if err := d.deployToFC3(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case "2.0":
|
case "2", "2.0":
|
||||||
if err := d.deployToFC2(ctx, certPem, privkeyPem); err != nil {
|
if err := d.deployToFC2(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, xerrors.Errorf("unsupported service version: %s", d.config.ServiceVersion)
|
return nil, fmt.Errorf("unsupported service version '%s'", d.config.ServiceVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, privkeyPem string) error {
|
func (d *DeployerProvider) deployToFC3(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
// 获取自定义域名
|
// 获取自定义域名
|
||||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-getcustomdomain
|
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-getcustomdomain
|
||||||
getCustomDomainResp, err := d.sdkClients.fc3.GetCustomDomain(tea.String(d.config.Domain))
|
getCustomDomainResp, err := d.sdkClients.FC3.GetCustomDomain(tea.String(d.config.Domain))
|
||||||
|
d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'")
|
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已获取自定义域名", getCustomDomainResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新自定义域名
|
// 更新自定义域名
|
||||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-updatecustomdomain
|
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-updatecustomdomain
|
||||||
updateCustomDomainReq := &aliyunFc3.UpdateCustomDomainRequest{
|
updateCustomDomainReq := &alifc3.UpdateCustomDomainRequest{
|
||||||
Body: &aliyunFc3.UpdateCustomDomainInput{
|
Body: &alifc3.UpdateCustomDomainInput{
|
||||||
CertConfig: &aliyunFc3.CertConfig{
|
CertConfig: &alifc3.CertConfig{
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
Certificate: tea.String(certPem),
|
Certificate: tea.String(certPEM),
|
||||||
PrivateKey: tea.String(privkeyPem),
|
PrivateKey: tea.String(privkeyPEM),
|
||||||
},
|
},
|
||||||
Protocol: getCustomDomainResp.Body.Protocol,
|
Protocol: getCustomDomainResp.Body.Protocol,
|
||||||
TlsConfig: getCustomDomainResp.Body.TlsConfig,
|
TlsConfig: getCustomDomainResp.Body.TlsConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
updateCustomDomainResp, err := d.sdkClients.fc3.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq)
|
updateCustomDomainResp, err := d.sdkClients.FC3.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq)
|
||||||
|
d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'")
|
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已更新自定义域名", updateCustomDomainResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, privkeyPem string) error {
|
func (d *DeployerProvider) deployToFC2(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
// 获取自定义域名
|
// 获取自定义域名
|
||||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-getcustomdomain
|
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-getcustomdomain
|
||||||
getCustomDomainResp, err := d.sdkClients.fc2.GetCustomDomain(tea.String(d.config.Domain))
|
getCustomDomainResp, err := d.sdkClients.FC2.GetCustomDomain(tea.String(d.config.Domain))
|
||||||
|
d.logger.Debug("sdk request 'fc.GetCustomDomain'", slog.Any("response", getCustomDomainResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'")
|
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已获取自定义域名", getCustomDomainResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新自定义域名
|
// 更新自定义域名
|
||||||
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-updatecustomdomain
|
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-updatecustomdomain
|
||||||
updateCustomDomainReq := &aliyunFc2.UpdateCustomDomainRequest{
|
updateCustomDomainReq := &alifc2.UpdateCustomDomainRequest{
|
||||||
CertConfig: &aliyunFc2.CertConfig{
|
CertConfig: &alifc2.CertConfig{
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
Certificate: tea.String(certPem),
|
Certificate: tea.String(certPEM),
|
||||||
PrivateKey: tea.String(privkeyPem),
|
PrivateKey: tea.String(privkeyPEM),
|
||||||
},
|
},
|
||||||
Protocol: getCustomDomainResp.Body.Protocol,
|
Protocol: getCustomDomainResp.Body.Protocol,
|
||||||
TlsConfig: getCustomDomainResp.Body.TlsConfig,
|
TlsConfig: getCustomDomainResp.Body.TlsConfig,
|
||||||
}
|
}
|
||||||
updateCustomDomainResp, err := d.sdkClients.fc2.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq)
|
updateCustomDomainResp, err := d.sdkClients.FC2.UpdateCustomDomain(tea.String(d.config.Domain), updateCustomDomainReq)
|
||||||
|
d.logger.Debug("sdk request 'fc.UpdateCustomDomain'", slog.Any("request", updateCustomDomainReq), slog.Any("response", updateCustomDomainResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'")
|
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已更新自定义域名", updateCustomDomainResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -157,30 +155,30 @@ func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients
|
|||||||
fc2Endpoint = fmt.Sprintf("fc.%s.aliyuncs.com", region)
|
fc2Endpoint = fmt.Sprintf("fc.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
fc2Config := &aliyunOpen.Config{
|
fc2Config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(fc2Endpoint),
|
Endpoint: tea.String(fc2Endpoint),
|
||||||
}
|
}
|
||||||
fc2Client, err := aliyunFc2.NewClient(fc2Config)
|
fc2Client, err := alifc2.NewClient(fc2Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接入点一览 https://api.aliyun.com/product/FC-Open
|
// 接入点一览 https://api.aliyun.com/product/FC-Open
|
||||||
fc3Endpoint := fmt.Sprintf("fcv3.%s.aliyuncs.com", region)
|
fc3Endpoint := fmt.Sprintf("fcv3.%s.aliyuncs.com", region)
|
||||||
fc3Config := &aliyunOpen.Config{
|
fc3Config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(fc3Endpoint),
|
Endpoint: tea.String(fc3Endpoint),
|
||||||
}
|
}
|
||||||
fc3Client, err := aliyunFc3.NewClient(fc3Config)
|
fc3Client, err := alifc3.NewClient(fc3Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &wSdkClients{
|
return &wSdkClients{
|
||||||
fc2: fc2Client,
|
FC2: fc2Client,
|
||||||
fc3: fc3Client,
|
FC3: fc3Client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunfc_test
|
package aliyunfc_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -17,7 +17,7 @@ var (
|
|||||||
fAccessKeyId string
|
fAccessKeyId string
|
||||||
fAccessKeySecret string
|
fAccessKeySecret string
|
||||||
fRegion string
|
fRegion string
|
||||||
fSiteId int64
|
fDomain string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
flag.Int64Var(&fSiteId, argsPrefix+"SITEID", "", "")
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -40,7 +40,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYID="your-access-key-id" \
|
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYID="your-access-key-id" \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYSECRET="your-access-key-secret" \
|
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNFC_REGION="cn-hangzhou" \
|
--CERTIMATE_DEPLOYER_ALIYUNFC_REGION="cn-hangzhou" \
|
||||||
--CERTIMATE_DEPLOYER_ALIYUNFC_SITEID="your-fc-site-id"
|
--CERTIMATE_DEPLOYER_ALIYUNFC_DOMAIN="example.com"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -53,14 +53,14 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
fmt.Sprintf("REGION: %v", fRegion),
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
fmt.Sprintf("SITEID: %v", fSiteId),
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
AccessKeyId: fAccessKeyId,
|
AccessKeyId: fAccessKeyId,
|
||||||
AccessKeySecret: fAccessKeySecret,
|
AccessKeySecret: fAccessKeySecret,
|
||||||
Region: fRegion,
|
Region: fRegion,
|
||||||
SiteId: fSiteId,
|
Domain: fDomain,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package aliyunlive
|
package aliyunlive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunLive "github.com/alibabacloud-go/live-20161101/client"
|
alilive "github.com/alibabacloud-go/live-20161101/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -28,8 +27,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunLive.Client
|
sdkClient *alilive.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
@@ -41,46 +40,49 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式
|
// "*.example.com" → ".example.com",适配阿里云 Live 要求的泛域名格式
|
||||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||||
|
|
||||||
// 设置域名证书
|
// 设置域名证书
|
||||||
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-setlivedomaincertificate
|
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-setlivedomaincertificate
|
||||||
setLiveDomainSSLCertificateReq := &aliyunLive.SetLiveDomainCertificateRequest{
|
setLiveDomainSSLCertificateReq := &alilive.SetLiveDomainCertificateRequest{
|
||||||
DomainName: tea.String(domain),
|
DomainName: tea.String(domain),
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
SSLPub: tea.String(certPem),
|
SSLPub: tea.String(certPEM),
|
||||||
SSLPri: tea.String(privkeyPem),
|
SSLPri: tea.String(privkeyPEM),
|
||||||
}
|
}
|
||||||
setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainSSLCertificateReq)
|
setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainSSLCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainSSLCertificateReq), slog.Any("response", setLiveDomainSSLCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'live.SetLiveDomainCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已设置域名证书", setLiveDomainSSLCertificateResp)
|
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunLive.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alilive.Client, error) {
|
||||||
// 接入点一览 https://api.aliyun.com/product/live
|
// 接入点一览 https://api.aliyun.com/product/live
|
||||||
var endpoint string
|
var endpoint string
|
||||||
switch region {
|
switch region {
|
||||||
@@ -97,13 +99,13 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunLive.C
|
|||||||
endpoint = fmt.Sprintf("live.%s.aliyuncs.com", region)
|
endpoint = fmt.Sprintf("live.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(endpoint),
|
Endpoint: tea.String(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunLive.NewClient(config)
|
client, err := alilive.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunlive_test
|
package aliyunlive_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package aliyunnlb
|
package aliyunnlb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
aliyunNlb "github.com/alibabacloud-go/nlb-20220430/v2/client"
|
alinlb "github.com/alibabacloud-go/nlb-20220430/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
)
|
)
|
||||||
@@ -36,8 +35,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunNlb.Client
|
sdkClient *alinlb.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,36 +49,41 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 上传证书到 CAS
|
// 上传证书到 CAS
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
|
||||||
|
|
||||||
// 根据部署资源类型决定部署方式
|
// 根据部署资源类型决定部署方式
|
||||||
switch d.config.ResourceType {
|
switch d.config.ResourceType {
|
||||||
case RESOURCE_TYPE_LOADBALANCER:
|
case RESOURCE_TYPE_LOADBALANCER:
|
||||||
@@ -93,7 +97,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
@@ -106,31 +110,37 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
// 查询负载均衡实例的详细信息
|
// 查询负载均衡实例的详细信息
|
||||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute
|
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute
|
||||||
getLoadBalancerAttributeReq := &aliyunNlb.GetLoadBalancerAttributeRequest{
|
getLoadBalancerAttributeReq := &alinlb.GetLoadBalancerAttributeRequest{
|
||||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||||
}
|
}
|
||||||
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'nlb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetLoadBalancerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'nlb.GetLoadBalancerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 NLB 负载均衡实例", getLoadBalancerAttributeResp)
|
|
||||||
|
|
||||||
// 查询 TCPSSL 监听列表
|
// 查询 TCPSSL 监听列表
|
||||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
|
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
|
||||||
listenerIds := make([]string, 0)
|
listenerIds := make([]string, 0)
|
||||||
listListenersLimit := int32(100)
|
listListenersLimit := int32(100)
|
||||||
var listListenersToken *string = nil
|
var listListenersToken *string = nil
|
||||||
for {
|
for {
|
||||||
listListenersReq := &aliyunNlb.ListListenersRequest{
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listListenersReq := &alinlb.ListListenersRequest{
|
||||||
MaxResults: tea.Int32(listListenersLimit),
|
MaxResults: tea.Int32(listListenersLimit),
|
||||||
NextToken: listListenersToken,
|
NextToken: listListenersToken,
|
||||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||||
ListenerProtocol: tea.String("TCPSSL"),
|
ListenerProtocol: tea.String("TCPSSL"),
|
||||||
}
|
}
|
||||||
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||||
|
d.logger.Debug("sdk request 'nlb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.ListListeners'")
|
return fmt.Errorf("failed to execute sdk request 'nlb.ListListeners': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
@@ -146,17 +156,22 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", listenerIds)
|
|
||||||
|
|
||||||
// 遍历更新监听证书
|
// 遍历更新监听证书
|
||||||
if len(listenerIds) == 0 {
|
if len(listenerIds) == 0 {
|
||||||
return errors.New("listener not found")
|
d.logger.Info("no nlb listeners to deploy")
|
||||||
} else {
|
} else {
|
||||||
|
d.logger.Info("found tcpssl listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,33 +199,31 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
|
|||||||
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
|
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
|
||||||
// 查询监听的属性
|
// 查询监听的属性
|
||||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute
|
||||||
getListenerAttributeReq := &aliyunNlb.GetListenerAttributeRequest{
|
getListenerAttributeReq := &alinlb.GetListenerAttributeRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
}
|
}
|
||||||
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'nlb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.GetListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'nlb.GetListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已查询到 NLB 监听配置", getListenerAttributeResp)
|
|
||||||
|
|
||||||
// 修改监听的属性
|
// 修改监听的属性
|
||||||
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute
|
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute
|
||||||
updateListenerAttributeReq := &aliyunNlb.UpdateListenerAttributeRequest{
|
updateListenerAttributeReq := &alinlb.UpdateListenerAttributeRequest{
|
||||||
ListenerId: tea.String(cloudListenerId),
|
ListenerId: tea.String(cloudListenerId),
|
||||||
CertificateIds: []*string{tea.String(cloudCertId)},
|
CertificateIds: []*string{tea.String(cloudCertId)},
|
||||||
}
|
}
|
||||||
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||||
|
d.logger.Debug("sdk request 'nlb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Wrap(err, "failed to execute sdk request 'nlb.UpdateListenerAttribute'")
|
return fmt.Errorf("failed to execute sdk request 'nlb.UpdateListenerAttribute': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("已更新 NLB 监听配置", updateListenerAttributeResp)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alinlb.Client, error) {
|
||||||
// 接入点一览 https://api.aliyun.com/product/Nlb
|
// 接入点一览 https://api.aliyun.com/product/Nlb
|
||||||
var endpoint string
|
var endpoint string
|
||||||
switch region {
|
switch region {
|
||||||
@@ -218,13 +231,13 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Cl
|
|||||||
endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
|
endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(endpoint),
|
Endpoint: tea.String(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunNlb.NewClient(config)
|
client, err := alinlb.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -238,7 +251,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
|
|||||||
// 阿里云 CAS 服务接入点是独立于 NLB 服务的
|
// 阿里云 CAS 服务接入点是独立于 NLB 服务的
|
||||||
// 国内版固定接入点:华东一杭州
|
// 国内版固定接入点:华东一杭州
|
||||||
// 国际版固定接入点:亚太东南一新加坡
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
casRegion = "ap-southeast-1"
|
casRegion = "ap-southeast-1"
|
||||||
} else {
|
} else {
|
||||||
casRegion = "cn-hangzhou"
|
casRegion = "cn-hangzhou"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunnlb_test
|
package aliyunnlb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunnlb
|
package aliyunnlb
|
||||||
|
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package aliyunoss
|
package aliyunoss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -27,7 +26,7 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *oss.Client
|
sdkClient *oss.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,22 +39,26 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
if d.config.Bucket == "" {
|
if d.config.Bucket == "" {
|
||||||
return nil, errors.New("config `bucket` is required")
|
return nil, errors.New("config `bucket` is required")
|
||||||
}
|
}
|
||||||
@@ -65,16 +68,18 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
|
|||||||
|
|
||||||
// 为存储空间绑定自定义域名
|
// 为存储空间绑定自定义域名
|
||||||
// REF: https://help.aliyun.com/zh/oss/developer-reference/putcname
|
// REF: https://help.aliyun.com/zh/oss/developer-reference/putcname
|
||||||
err := d.sdkClient.PutBucketCnameWithCertificate(d.config.Bucket, oss.PutBucketCname{
|
putBucketCnameWithCertificateReq := oss.PutBucketCname{
|
||||||
Cname: d.config.Domain,
|
Cname: d.config.Domain,
|
||||||
CertificateConfiguration: &oss.CertificateConfiguration{
|
CertificateConfiguration: &oss.CertificateConfiguration{
|
||||||
Certificate: certPem,
|
Certificate: certPEM,
|
||||||
PrivateKey: privkeyPem,
|
PrivateKey: privkeyPEM,
|
||||||
Force: true,
|
Force: true,
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
err := d.sdkClient.PutBucketCnameWithCertificate(d.config.Bucket, putBucketCnameWithCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'oss.PutBucketCnameWithCertificate'", slog.Any("bucket", d.config.Bucket), slog.Any("request", putBucketCnameWithCertificateReq))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'oss.PutBucketCnameWithCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'oss.PutBucketCnameWithCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunoss_test
|
package aliyunoss_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package aliyunvod
|
package aliyunvod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
aliyunVod "github.com/alibabacloud-go/vod-20170321/v4/client"
|
alivod "github.com/alibabacloud-go/vod-20170321/v4/client"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -27,8 +26,8 @@ type DeployerConfig struct {
|
|||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunVod.Client
|
sdkClient *alivod.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
@@ -40,53 +39,56 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 设置域名证书
|
// 设置域名证书
|
||||||
// REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate
|
// REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate
|
||||||
setVodDomainSSLCertificateReq := &aliyunVod.SetVodDomainSSLCertificateRequest{
|
setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{
|
||||||
DomainName: tea.String(d.config.Domain),
|
DomainName: tea.String(d.config.Domain),
|
||||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||||
CertType: tea.String("upload"),
|
CertType: tea.String("upload"),
|
||||||
SSLProtocol: tea.String("on"),
|
SSLProtocol: tea.String("on"),
|
||||||
SSLPub: tea.String(certPem),
|
SSLPub: tea.String(certPEM),
|
||||||
SSLPri: tea.String(privkeyPem),
|
SSLPri: tea.String(privkeyPEM),
|
||||||
}
|
}
|
||||||
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq)
|
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'live.SetVodDomainSSLCertificate'", slog.Any("request", setVodDomainSSLCertificateReq), slog.Any("response", setVodDomainSSLCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetVodDomainSSLCertificate'")
|
return nil, fmt.Errorf("failed to execute sdk request 'live.SetVodDomainSSLCertificate': %w", err)
|
||||||
} else {
|
|
||||||
d.logger.Logt("已设置域名证书", setVodDomainSSLCertificateResp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunVod.Client, error) {
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alivod.Client, error) {
|
||||||
// 接入点一览 https://api.aliyun.com/product/vod
|
// 接入点一览 https://api.aliyun.com/product/vod
|
||||||
endpoint := fmt.Sprintf("vod.%s.aliyuncs.com", region)
|
endpoint := fmt.Sprintf("vod.%s.aliyuncs.com", region)
|
||||||
|
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(endpoint),
|
Endpoint: tea.String(endpoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunVod.NewClient(config)
|
client, err := alivod.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package aliyunvod_test
|
package aliyunvod_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package aliyunwaf
|
package aliyunwaf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
"github.com/alibabacloud-go/tea/tea"
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
aliyunWaf "github.com/alibabacloud-go/waf-openapi-20211001/v5/client"
|
aliwaf "github.com/alibabacloud-go/waf-openapi-20211001/v5/client"
|
||||||
xerrors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/logger"
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
@@ -24,14 +24,18 @@ type DeployerConfig struct {
|
|||||||
AccessKeySecret string `json:"accessKeySecret"`
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
// 阿里云地域。
|
// 阿里云地域。
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
// 阿里云 WAF 实例 ID。
|
// 服务版本。
|
||||||
|
ServiceVersion string `json:"serviceVersion"`
|
||||||
|
// WAF 实例 ID。
|
||||||
InstanceId string `json:"instanceId"`
|
InstanceId string `json:"instanceId"`
|
||||||
|
// 接入域名(支持泛域名)。
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger logger.Logger
|
logger *slog.Logger
|
||||||
sdkClient *aliyunWaf.Client
|
sdkClient *aliwaf.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,81 +48,137 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
|
|
||||||
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create sdk client")
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DeployerProvider{
|
return &DeployerProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.NewNilLogger(),
|
logger: slog.Default(),
|
||||||
sdkClient: client,
|
sdkClient: client,
|
||||||
sslUploader: uploader,
|
sslUploader: uploader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
d.logger = logger
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
if d.config.InstanceId == "" {
|
if d.config.InstanceId == "" {
|
||||||
return nil, errors.New("config `instanceId` is required")
|
return nil, errors.New("config `instanceId` is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传证书到 CAS
|
switch d.config.ServiceVersion {
|
||||||
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
|
case "3", "3.0":
|
||||||
if err != nil {
|
if err := d.deployToWAF3(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
return nil, xerrors.Wrap(err, "failed to upload certificate file")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Logt("certificate file uploaded", upres)
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported service version '%s'", d.config.ServiceVersion)
|
||||||
// 查询默认 SSL/TLS 设置
|
|
||||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedefaulthttps
|
|
||||||
describeDefaultHttpsReq := &aliyunWaf.DescribeDefaultHttpsRequest{
|
|
||||||
InstanceId: tea.String(d.config.InstanceId),
|
|
||||||
RegionId: tea.String(d.config.Region),
|
|
||||||
}
|
}
|
||||||
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttps(describeDefaultHttpsReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDefaultHttps'")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.logger.Logt("已查询到默认 SSL/TLS 设置", describeDefaultHttpsResp)
|
|
||||||
|
|
||||||
// 修改默认 SSL/TLS 设置
|
|
||||||
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydefaulthttps
|
|
||||||
modifyDefaultHttpsReq := &aliyunWaf.ModifyDefaultHttpsRequest{
|
|
||||||
InstanceId: tea.String(d.config.InstanceId),
|
|
||||||
RegionId: tea.String(d.config.Region),
|
|
||||||
CertId: tea.String(upres.CertId),
|
|
||||||
TLSVersion: describeDefaultHttpsResp.Body.DefaultHttps.TLSVersion,
|
|
||||||
EnableTLSv3: describeDefaultHttpsResp.Body.DefaultHttps.EnableTLSv3,
|
|
||||||
}
|
|
||||||
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttps(modifyDefaultHttpsReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDefaultHttps'")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.logger.Logt("已修改默认 SSL/TLS 设置", modifyDefaultHttpsResp)
|
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunWaf.Client, error) {
|
func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
// 未指定接入域名,只需替换默认证书即可
|
||||||
|
|
||||||
|
// 查询默认 SSL/TLS 设置
|
||||||
|
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedefaulthttps
|
||||||
|
describeDefaultHttpsReq := &aliwaf.DescribeDefaultHttpsRequest{
|
||||||
|
InstanceId: tea.String(d.config.InstanceId),
|
||||||
|
RegionId: tea.String(d.config.Region),
|
||||||
|
}
|
||||||
|
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttps(describeDefaultHttpsReq)
|
||||||
|
d.logger.Debug("sdk request 'waf.DescribeDefaultHttps'", slog.Any("request", describeDefaultHttpsReq), slog.Any("response", describeDefaultHttpsResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDefaultHttps': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改默认 SSL/TLS 设置
|
||||||
|
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydefaulthttps
|
||||||
|
modifyDefaultHttpsReq := &aliwaf.ModifyDefaultHttpsRequest{
|
||||||
|
InstanceId: tea.String(d.config.InstanceId),
|
||||||
|
RegionId: tea.String(d.config.Region),
|
||||||
|
CertId: tea.String(upres.CertId),
|
||||||
|
TLSVersion: tea.String("tlsv1"),
|
||||||
|
EnableTLSv3: tea.Bool(false),
|
||||||
|
}
|
||||||
|
if describeDefaultHttpsResp.Body != nil && describeDefaultHttpsResp.Body.DefaultHttps != nil {
|
||||||
|
modifyDefaultHttpsReq.TLSVersion = describeDefaultHttpsResp.Body.DefaultHttps.TLSVersion
|
||||||
|
modifyDefaultHttpsReq.EnableTLSv3 = describeDefaultHttpsResp.Body.DefaultHttps.EnableTLSv3
|
||||||
|
}
|
||||||
|
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttps(modifyDefaultHttpsReq)
|
||||||
|
d.logger.Debug("sdk request 'waf.ModifyDefaultHttps'", slog.Any("request", modifyDefaultHttpsReq), slog.Any("response", modifyDefaultHttpsResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDefaultHttps': %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 指定接入域名
|
||||||
|
|
||||||
|
// 查询 CNAME 接入详情
|
||||||
|
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedomaindetail
|
||||||
|
describeDomainDetailReq := &aliwaf.DescribeDomainDetailRequest{
|
||||||
|
InstanceId: tea.String(d.config.InstanceId),
|
||||||
|
RegionId: tea.String(d.config.Region),
|
||||||
|
Domain: tea.String(d.config.Domain),
|
||||||
|
}
|
||||||
|
describeDomainDetailResp, err := d.sdkClient.DescribeDomainDetail(describeDomainDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'waf.DescribeDomainDetail'", slog.Any("request", describeDomainDetailReq), slog.Any("response", describeDomainDetailResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDomainDetail': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 CNAME 接入资源
|
||||||
|
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydomain
|
||||||
|
modifyDomainReq := &aliwaf.ModifyDomainRequest{
|
||||||
|
InstanceId: tea.String(d.config.InstanceId),
|
||||||
|
RegionId: tea.String(d.config.Region),
|
||||||
|
Domain: tea.String(d.config.Domain),
|
||||||
|
Listen: &aliwaf.ModifyDomainRequestListen{CertId: tea.String(upres.ExtendedData["certIdentifier"].(string))},
|
||||||
|
Redirect: &aliwaf.ModifyDomainRequestRedirect{Loadbalance: tea.String("iphash")},
|
||||||
|
}
|
||||||
|
modifyDomainReq = assign(modifyDomainReq, describeDomainDetailResp.Body)
|
||||||
|
modifyDomainResp, err := d.sdkClient.ModifyDomain(modifyDomainReq)
|
||||||
|
d.logger.Debug("sdk request 'waf.ModifyDomain'", slog.Any("request", modifyDomainReq), slog.Any("response", modifyDomainResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDomain': %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliwaf.Client, error) {
|
||||||
// 接入点一览:https://api.aliyun.com/product/waf-openapi
|
// 接入点一览:https://api.aliyun.com/product/waf-openapi
|
||||||
config := &aliyunOpen.Config{
|
config := &aliopen.Config{
|
||||||
AccessKeyId: tea.String(accessKeyId),
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
AccessKeySecret: tea.String(accessKeySecret),
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
Endpoint: tea.String(fmt.Sprintf("wafopenapi.%s.aliyuncs.com", region)),
|
Endpoint: tea.String(fmt.Sprintf("wafopenapi.%s.aliyuncs.com", region)),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := aliyunWaf.NewClient(config)
|
client, err := aliwaf.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -132,7 +192,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
|
|||||||
// 阿里云 CAS 服务接入点是独立于 WAF 服务的
|
// 阿里云 CAS 服务接入点是独立于 WAF 服务的
|
||||||
// 国内版固定接入点:华东一杭州
|
// 国内版固定接入点:华东一杭州
|
||||||
// 国际版固定接入点:亚太东南一新加坡
|
// 国际版固定接入点:亚太东南一新加坡
|
||||||
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
|
if !strings.HasPrefix(casRegion, "cn-") {
|
||||||
casRegion = "ap-southeast-1"
|
casRegion = "ap-southeast-1"
|
||||||
} else {
|
} else {
|
||||||
casRegion = "cn-hangzhou"
|
casRegion = "cn-hangzhou"
|
||||||
@@ -146,3 +206,166 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
|
|||||||
})
|
})
|
||||||
return uploader, err
|
return uploader, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assign(source *aliwaf.ModifyDomainRequest, target *aliwaf.DescribeDomainDetailResponseBody) *aliwaf.ModifyDomainRequest {
|
||||||
|
// `ModifyDomain` 中不传的字段表示使用默认值、而非保留原值,
|
||||||
|
// 因此这里需要把原配置中的参数重新赋值回去。
|
||||||
|
|
||||||
|
if target == nil {
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen != nil {
|
||||||
|
if source.Listen == nil {
|
||||||
|
source.Listen = &aliwaf.ModifyDomainRequestListen{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.CipherSuite != nil {
|
||||||
|
source.Listen.CipherSuite = tea.Int32(int32(*target.Listen.CipherSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.CustomCiphers != nil {
|
||||||
|
source.Listen.CustomCiphers = target.Listen.CustomCiphers
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.EnableTLSv3 != nil {
|
||||||
|
source.Listen.EnableTLSv3 = target.Listen.EnableTLSv3
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.ExclusiveIp != nil {
|
||||||
|
source.Listen.ExclusiveIp = target.Listen.ExclusiveIp
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.FocusHttps != nil {
|
||||||
|
source.Listen.FocusHttps = target.Listen.FocusHttps
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.Http2Enabled != nil {
|
||||||
|
source.Listen.Http2Enabled = target.Listen.Http2Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.HttpPorts != nil {
|
||||||
|
source.Listen.HttpPorts = sliceutil.Map(target.Listen.HttpPorts, func(v *int64) *int32 {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tea.Int32(int32(*v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.HttpsPorts != nil {
|
||||||
|
source.Listen.HttpsPorts = sliceutil.Map(target.Listen.HttpsPorts, func(v *int64) *int32 {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tea.Int32(int32(*v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.IPv6Enabled != nil {
|
||||||
|
source.Listen.IPv6Enabled = target.Listen.IPv6Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.ProtectionResource != nil {
|
||||||
|
source.Listen.ProtectionResource = target.Listen.ProtectionResource
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.TLSVersion != nil {
|
||||||
|
source.Listen.TLSVersion = target.Listen.TLSVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.XffHeaderMode != nil {
|
||||||
|
source.Listen.XffHeaderMode = tea.Int32(int32(*target.Listen.XffHeaderMode))
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Listen.XffHeaders != nil {
|
||||||
|
source.Listen.XffHeaders = target.Listen.XffHeaders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect != nil {
|
||||||
|
if source.Redirect == nil {
|
||||||
|
source.Redirect = &aliwaf.ModifyDomainRequestRedirect{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.Backends != nil {
|
||||||
|
source.Redirect.Backends = sliceutil.Map(target.Redirect.Backends, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectBackends) *string {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.Backend
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.BackupBackends != nil {
|
||||||
|
source.Redirect.BackupBackends = sliceutil.Map(target.Redirect.BackupBackends, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectBackupBackends) *string {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.Backend
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.ConnectTimeout != nil {
|
||||||
|
source.Redirect.ConnectTimeout = target.Redirect.ConnectTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.FocusHttpBackend != nil {
|
||||||
|
source.Redirect.FocusHttpBackend = target.Redirect.FocusHttpBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.Keepalive != nil {
|
||||||
|
source.Redirect.Keepalive = target.Redirect.Keepalive
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.KeepaliveRequests != nil {
|
||||||
|
source.Redirect.KeepaliveRequests = target.Redirect.KeepaliveRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.KeepaliveTimeout != nil {
|
||||||
|
source.Redirect.KeepaliveTimeout = target.Redirect.KeepaliveTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.Loadbalance != nil {
|
||||||
|
source.Redirect.Loadbalance = target.Redirect.Loadbalance
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.ReadTimeout != nil {
|
||||||
|
source.Redirect.ReadTimeout = target.Redirect.ReadTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.RequestHeaders != nil {
|
||||||
|
source.Redirect.RequestHeaders = sliceutil.Map(target.Redirect.RequestHeaders, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectRequestHeaders) *aliwaf.ModifyDomainRequestRedirectRequestHeaders {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &aliwaf.ModifyDomainRequestRedirectRequestHeaders{
|
||||||
|
Key: v.Key,
|
||||||
|
Value: v.Value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.Retry != nil {
|
||||||
|
source.Redirect.Retry = target.Redirect.Retry
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.SniEnabled != nil {
|
||||||
|
source.Redirect.SniEnabled = target.Redirect.SniEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.SniHost != nil {
|
||||||
|
source.Redirect.SniHost = target.Redirect.SniHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.WriteTimeout != nil {
|
||||||
|
source.Redirect.WriteTimeout = target.Redirect.WriteTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.Redirect.XffProto != nil {
|
||||||
|
source.Redirect.XffProto = target.Redirect.XffProto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user