Compare commits

...

161 Commits

Author SHA1 Message Date
RHQYZ
c34346cb31 bump version to v0.3.13 2025-05-21 00:20:08 +08:00
RHQYZ
7643975ef9 Merge pull request #714 from fudiwei/bugfix
bugfix
2025-05-20 23:14:06 +08:00
Fu Diwei
799ad61dcc fix: #713 2025-05-20 23:10:44 +08:00
Fu Diwei
1c3cb1b21b fix: #710 2025-05-20 23:04:34 +08:00
Fu Diwei
9d9ca88ebe fix: tsc build error 2025-05-20 23:04:28 +08:00
Fu Diwei
d81a33f24a fix: #704 2025-05-20 21:52:33 +08:00
Fu Diwei
398337826e fix: #706 2025-05-20 21:51:38 +08:00
RHQYZ
7469310fdb Merge pull request #699 from Lensual/feat-ssh-jumpserver
feat: ssh jump server support (#49) (#95)
2025-05-20 21:42:53 +08:00
RHQYZ
fe993b54f3 Merge branch 'main' into feat-ssh-jumpserver 2025-05-20 21:42:38 +08:00
RHQYZ
1ed1c62f76 Merge pull request #697 from fudiwei/main
new providers
2025-05-20 21:41:07 +08:00
Fu Diwei
524c4fd1e8 feat: new deployment provider: baotawaf console 2025-05-20 21:27:26 +08:00
Fu Diwei
591df58992 feat: new deployment provider: baotawaf site 2025-05-20 21:27:26 +08:00
Fu Diwei
4ad08d983a refactor: clean code 2025-05-20 21:27:26 +08:00
Fu Diwei
7a663d31cb feat: new deployment provider: wangsu cdn 2025-05-20 21:27:26 +08:00
Fu Diwei
e6fc92eccb feat: new deployment provider: wangsu certificate management 2025-05-20 21:27:19 +08:00
Fu Diwei
c9e6bd0c2f feat: new deployment provider: lecdn 2025-05-19 22:51:17 +08:00
神楽坂ニャン
36dd4ef3eb update: update formSchema for jump server & fix username validate message 2025-05-19 14:27:35 +08:00
Fu Diwei
a66e1c04c9 feat(ui): allow clear input when field is optional 2025-05-19 11:39:45 +08:00
Fu Diwei
3098f6a82f feat: rename certificate props 'issuer' to 'issuerOrg' 2025-05-19 10:35:05 +08:00
神楽坂ニャン
4a8eaa9ffa feat: ssh jump server support 2025-05-17 19:47:31 +08:00
Fu Diwei
3462e454d0 feat: new deployment provider: aliyun ga 2025-05-16 23:30:03 +08:00
Fu Diwei
eabd16dd71 feat: new deployment provider: flexcdn 2025-05-16 22:18:03 +08:00
Fu Diwei
122d766cab feat: new ca provider: custom acme ca 2025-05-16 21:40:40 +08:00
Fu Diwei
980d1ee0b9 feat: new deployment providers: ratpanel console & site 2025-05-16 20:15:59 +08:00
RHQYZ
e9f248d8ec Merge branch 'usual2970:main' into main 2025-05-16 19:23:03 +08:00
RHQYZ
2906576de0 Merge pull request #696 from devhaozi/main
feat: backend support for ratpanel
2025-05-16 19:22:15 +08:00
Fu Diwei
fd875feef3 feat: support 1panel v2 2025-05-16 18:57:39 +08:00
耗子
871d3ece90 fix: test case 2025-05-16 18:51:03 +08:00
耗子
8014abc836 feat: add test cases 2025-05-16 18:43:50 +08:00
Fu Diwei
0840454143 update README 2025-05-16 15:10:44 +08:00
RHQYZ
06fd95782a Merge pull request #693 from fudiwei/main
refactor
2025-05-16 15:07:21 +08:00
Fu Diwei
ef0f0f6b43 refactor: remove nikoksr/notify deps 2025-05-16 15:04:39 +08:00
Fu Diwei
b15bf8ef98 refactor: clean code 2025-05-16 13:56:45 +08:00
Fu Diwei
dd2087b101 refactor: clean code 2025-05-16 11:55:07 +08:00
RHQYZ
12d0b66c61 Merge pull request #691 from fudiwei/main
refactor
2025-05-16 11:15:38 +08:00
Fu Diwei
f3bbb9e8b2 refactor: optimize third-party sdks 2025-05-16 11:10:43 +08:00
RHQYZ
dcd646b465 Merge branch 'usual2970:main' into main 2025-05-16 02:52:51 +08:00
耗子
bd4aa4806f feat: backend support for ratpanel 2025-05-16 02:33:46 +08:00
Fu Diwei
b122bbced9 build: config goreleaser 2025-05-16 02:26:38 +08:00
Fu Diwei
5edae845cb build: config goreleaser 2025-05-16 02:03:49 +08:00
Fu Diwei
b7af3c10e4 build: config goreleaser 2025-05-16 01:58:53 +08:00
Fu Diwei
2453048288 build: config goreleaser 2025-05-16 01:50:43 +08:00
Fu Diwei
221b6a6fc6 feat(ui): improve i18n 2025-05-16 01:34:05 +08:00
Fu Diwei
851ad70a6c bump version to v0.3.12 2025-05-15 23:52:21 +08:00
Fu Diwei
1844e9a9db update README 2025-05-15 23:41:25 +08:00
RHQYZ
04e9f4e909 Merge pull request #689 from fudiwei/feat/providers
new providers
2025-05-15 23:35:48 +08:00
Fu Diwei
e6e2587bfc feat: new deployment provider: netlify site 2025-05-15 23:25:48 +08:00
Fu Diwei
70bd2f0581 feat: new acme dns-01 provider: netlify 2025-05-15 23:25:48 +08:00
Fu Diwei
9e08cfd1d1 feat: support replacing old certificate on deployment to aws acm 2025-05-15 23:25:48 +08:00
Fu Diwei
cd93a2d72c feat: new acme dns-01 provider: netcup 2025-05-15 23:25:42 +08:00
RHQYZ
11a4d4f55c Merge pull request #684 from fudiwei/main
enhance & bugfix
2025-05-15 21:16:17 +08:00
RHQYZ
f33570d514 Merge branch 'usual2970:main' into main 2025-05-15 21:15:49 +08:00
Fu Diwei
1ee3b64a19 feat: config goedge api user role 2025-05-15 21:05:22 +08:00
Fu Diwei
a3a56f3346 feat: add preset scripts for synologydsm and fnos on deployment to ssh 2025-05-15 20:52:43 +08:00
Fu Diwei
564ed8f0d3 fix: #686 2025-05-15 17:52:24 +08:00
Fu Diwei
268ec4bd7f feat(ui): browser happy detecting 2025-05-15 02:20:47 +08:00
Fu Diwei
e55e6cc512 fix(ui): tsc build error 2025-05-15 01:57:00 +08:00
Fu Diwei
6c6bb78568 feat: deploy server certificate or intermedia certificate 2025-05-15 01:40:37 +08:00
Fu Diwei
178c62512d fix(ui): fix incorrect webhook guide 2025-05-15 01:16:29 +08:00
Fu Diwei
9eaf9fd933 feat(ui): CodeInput 2025-05-15 00:58:02 +08:00
Fu Diwei
04abf9dd76 feat(ui): TextFileInput 2025-05-14 00:42:59 +08:00
Fu Diwei
355059df3c fix(ui): disallow to create access of builtin providers 2025-05-13 22:32:33 +08:00
Fu Diwei
4a6c32877f chore: github issue templates 2025-05-13 22:00:15 +08:00
Fu Diwei
258f6b5001 fix: resolve #681 2025-05-13 21:33:59 +08:00
Fu Diwei
78d9501fce fix: fix typo 2025-05-13 21:22:01 +08:00
RHQYZ
15975bb92c Merge branch 'usual2970:main' into main 2025-05-13 21:19:13 +08:00
Fu Diwei
fef8c3cb1a build: set go version to 1.24 2025-05-13 01:33:39 +08:00
RHQYZ
4b226d7730 Merge branch 'usual2970:main' into main 2025-05-13 01:00:07 +08:00
RHQYZ
41abc8cab8 bump version to v0.3.11 2025-05-13 00:54:08 +08:00
Fu Diwei
4d3b3834f5 fix(ui): tsc build error 2025-05-13 00:52:41 +08:00
Fu Diwei
9b1ba574ff fix(ui): scale origin 2025-05-13 00:52:41 +08:00
Fu Diwei
ee7d950c15 feat(ui): max content height on AccessEditModal 2025-05-13 00:52:41 +08:00
Fu Diwei
8ca41f18be feat(ui): show search on AccessSelect 2025-05-13 00:52:41 +08:00
Fu Diwei
9d522bd9ef feat(ui): AccessProviderPicker 2025-05-13 00:52:41 +08:00
Fu Diwei
f9633616e2 feat: support replacing old certificate on deployment to gcore cdn 2025-05-13 00:52:41 +08:00
Fu Diwei
9a0dd53cd7 chore(deps): upgrade gomod dependencies 2025-05-13 00:52:41 +08:00
Fu Diwei
6bbcec6163 chore(deps): upgrade npm dependencies 2025-05-13 00:52:41 +08:00
Fu Diwei
fdee69bdaf feat(ui): move settings page entry from topbar to sider-menu 2025-05-13 00:52:41 +08:00
Fu Diwei
2ec4adf7d5 fix(ui): tsc build error 2025-05-13 00:52:05 +08:00
Fu Diwei
709684d00f fix(ui): scale origin 2025-05-13 00:43:58 +08:00
Fu Diwei
989fd1ec6d feat(ui): max content height on AccessEditModal 2025-05-13 00:39:18 +08:00
Fu Diwei
7d57c5abc0 feat(ui): show search on AccessSelect 2025-05-13 00:33:48 +08:00
Fu Diwei
0c42bb845d feat(ui): AccessProviderPicker 2025-05-13 00:28:58 +08:00
Fu Diwei
07037e8d49 feat: support replacing old certificate on deployment to gcore cdn 2025-05-12 23:19:13 +08:00
Fu Diwei
a1fc3841df chore(deps): upgrade gomod dependencies 2025-05-12 23:00:39 +08:00
Fu Diwei
1fee156457 chore(deps): upgrade npm dependencies 2025-05-12 22:12:59 +08:00
Fu Diwei
82bdccf7e7 feat(ui): move settings page entry from topbar to sider-menu 2025-05-12 21:34:00 +08:00
Fu Diwei
7936c34472 fix #672 2025-05-12 20:19:00 +08:00
RHQYZ
365fd0bd5b Merge pull request #670 from fudiwei/feat/providers
new providers
2025-05-09 21:52:26 +08:00
Fu Diwei
f63ee41405 feat: new deployment provider: proxmoxve 2025-05-09 20:53:25 +08:00
Fu Diwei
26359b9d16 feat: allow insecure connections in cdnfly, goedge, powerdns 2025-05-09 16:35:58 +08:00
RHQYZ
5abdb577fb Merge pull request #666 from fudiwei/feat/providers
new providers
2025-05-09 11:00:32 +08:00
RHQYZ
c67c4741e5 bump version to v0.3.10 2025-05-08 21:04:14 +08:00
RHQYZ
60bc4be9ea Merge pull request #664 from fudiwei/bugfix
bugfix
2025-05-08 21:02:18 +08:00
RHQYZ
b83f87b6c8 Merge pull request #665 from fudiwei/main
Support configuring dns propagation waiting time
2025-05-08 21:01:50 +08:00
Fu Diwei
0a73ba1a53 feat: add preset scripts 2025-05-08 20:43:09 +08:00
Fu Diwei
a4d397f24b fix: fix typo 2025-05-08 15:36:51 +08:00
Fu Diwei
809f231981 feat: set the default max workers to the number of available CPU cores 2025-05-07 22:15:11 +08:00
Fu Diwei
1499c637ee feat: new deployment provider: goedge 2025-05-07 22:06:43 +08:00
Fu Diwei
e5805a028b feat: new acme dns-01 provider: aliyun esa 2025-05-07 22:06:43 +08:00
Fu Diwei
5cb0463cf6 feat: set the default max workers to the number of available CPU cores 2025-05-07 22:06:43 +08:00
Fu Diwei
12c208cad4 feat: new deployment provider: aliyun ddoscoo 2025-05-07 22:06:31 +08:00
Fu Diwei
1946a4e68a feat: add dns propagation wait configuration to application node in workflows 2025-05-07 11:56:35 +08:00
Fu Diwei
dfed0af053 fix: #658 2025-05-07 10:12:13 +08:00
Yoan.liu
fc064aa9f8 Merge pull request #643 from fudiwei/main
Support configuring independent notification channel for each workflow
2025-04-30 21:37:03 +08:00
Fu Diwei
edaf08361f refactor: clean code 2025-04-28 12:09:28 +08:00
Fu Diwei
92c93822b9 feat: webhook general template 2025-04-28 10:52:58 +08:00
Fu Diwei
18a19096d3 feat: add dingtalk, lark, and wecom bot webhook 2025-04-27 23:44:01 +08:00
Fu Diwei
7e707cd973 feat: webhook preset template data 2025-04-27 22:44:10 +08:00
Fu Diwei
d33b8caf14 feat: support overwriting the default webhook data of deployers and notifiers 2025-04-27 22:06:04 +08:00
Fu Diwei
e533f9407f feat: reserve accesses for ca or notification 2025-04-27 11:41:09 +08:00
Fu Diwei
193a19b79c feat: support more content-type in webhook 2025-04-27 00:01:00 +08:00
Fu Diwei
609a252ee0 refactor: clean code 2025-04-26 21:27:53 +08:00
Fu Diwei
3be70c3696 feat: support configuring method and headers in webhook 2025-04-26 21:17:09 +08:00
Fu Diwei
3c2fbd720f feat: support overwriting the default config of notifiers 2025-04-25 11:51:19 +08:00
Fu Diwei
11b413d0dc feat: adapt email, mattermost, telegram, and webhook to new notifier module 2025-04-24 23:37:27 +08:00
Fu Diwei
a117fd7d93 refactor: clean code 2025-04-24 23:14:17 +08:00
Fu Diwei
794695c313 refactor: clean code 2025-04-24 21:04:55 +08:00
Fu Diwei
7478dd7f47 feat: deprecate old notification module and introduce new notifier module 2025-04-24 20:27:20 +08:00
Fu Diwei
2d17501072 refactor: clean code 2025-04-24 10:46:16 +08:00
Fu Diwei
034bb71b10 feat(ui): show ca provider global settings button only when not specified ca provider 2025-04-24 09:13:49 +08:00
Fu Diwei
97f102533c feat: enhance context cancellation handling 2025-04-23 19:32:21 +08:00
RHQYZ
a90b6a8589 Merge pull request #640 from fudiwei/main
field type conversion
2025-04-22 22:28:45 +08:00
Fu Diwei
e8f6c665f9 Merge branch 'upstream' 2025-04-22 22:16:01 +08:00
Fu Diwei
6dac89e9a1 refactor: remove pkg/errors 2025-04-22 22:12:58 +08:00
Fu Diwei
4f512a6cdd refactor: clean code 2025-04-22 21:51:36 +08:00
Fu Diwei
94f162c189 Merge branch 'sync-upstream' 2025-04-22 21:27:37 +08:00
Fu Diwei
f5807d215f style: format code 2025-04-22 21:24:48 +08:00
Fu Diwei
3189e65bad refactor: clean code 2025-04-22 21:18:16 +08:00
Fu Diwei
3febc53061 feat: change field provider's type of table access 2025-04-22 18:34:51 +08:00
Fu Diwei
efd8135341 chore(deps): upgrade gomod dependencies 2025-04-22 16:25:18 +08:00
Fu Diwei
56405eff6c chore(deps): upgrade npm dependencies 2025-04-22 16:25:08 +08:00
Fu Diwei
e0fad9d9a9 Merge branch 'main' of https://github.com/fudiwei/certimate 2025-04-22 10:27:08 +08:00
Yoan.liu
8fe942d8d5 update to version v0.3.9 2025-04-21 21:27:13 +08:00
Yoan.liu
cf98e789d8 Merge pull request #627 from fudiwei/bugfix
bugfix
2025-04-21 17:50:02 +08:00
Fu Diwei
ff58b9a317 fix: wangsu api error 2025-04-21 09:17:52 +08:00
Yoan.liu
65e7d390b8 Merge pull request #629 from fudiwei/feat/providers
new providers
2025-04-20 08:22:56 +08:00
Fu Diwei
54ae378e30 fix: handle deployment task status check on deployment to wangsu cdnpro 2025-04-19 19:37:31 +08:00
Fu Diwei
347695cf66 feat: update default certificate paths on deployment to local and ssh 2025-04-19 14:04:06 +08:00
Fu Diwei
8f4d854b0d feat: support replacing old certificate on deployment to 1panel site 2025-04-19 14:02:55 +08:00
Yoan.liu
94bd846726 Merge pull request #625 from fondoger/fondoger/fix-volcengine
修复火山云证书上传获取空值的bug
2025-04-19 10:46:26 +08:00
Yoan.liu
0365841549 Merge pull request #621 from fondoger/fondoger/keyvault
Fix Azure KeyVault bug & Support custom certificate name in Azure KeyVault
2025-04-19 10:46:07 +08:00
Yoan.liu
74bd1f64a0 Merge pull request #610 from imlonghao/feat/bunny
feat: support bunny as dns and cdn provider
2025-04-19 10:45:51 +08:00
Fu Diwei
5bce03410e feat: add aliyun apigw deployer 2025-04-18 20:51:23 +08:00
Fu Diwei
32ff658e84 fix: #626 2025-04-18 18:20:01 +08:00
Fu Diwei
c10ceed753 feat: improve log 2025-04-18 18:04:52 +08:00
Fu Diwei
eb45b56a87 fix: ignore wangsu api responses without content 2025-04-18 17:54:51 +08:00
Fu Diwei
283b150d60 refactor: re-impl azure keyvault deployer 2025-04-18 17:52:12 +08:00
Fu Diwei
7329a22132 chore(ui): improve i18n 2025-04-17 22:11:45 +08:00
Fu Diwei
50b48d956f fix: #617 2025-04-17 22:08:53 +08:00
Fu Diwei
55d7a05af8 fix: #615 2025-04-17 21:44:40 +08:00
RHQYZ
6c70b0655a chore: modify error log 2025-04-17 13:39:04 +08:00
fondoger
0004eac764 Modify code according to code suggestions 2025-04-17 13:13:23 +08:00
fondoger
5fe24465d7 修复火山云证书上传获取空值的bug 2025-04-17 12:54:43 +08:00
fondoger
364ceb2399 Fix Azure KeyVault bug 2025-04-16 21:53:19 +08:00
imlonghao
5143823e43 feat: support bunny as dns and cdn provider 2025-04-13 15:47:23 +08:00
RHQYZ
363e23dd13 Merge branch 'usual2970:main' into main 2025-04-13 11:57:30 +08:00
RHQYZ
abe755cb69 Merge branch 'usual2970:main' into main 2025-04-09 22:56:26 +08:00
RHQYZ
cedbaf70c0 Merge branch 'usual2970:main' into main 2025-04-07 12:42:13 +08:00
Fu Diwei
2899aa1b19 feat: set placeholder values in preset scripts 2025-04-03 21:12:21 +08:00
Fu Diwei
ecff16c89d chore: improve error messages 2025-04-03 21:03:43 +08:00
609 changed files with 19238 additions and 5378 deletions

View File

@@ -10,5 +10,7 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.go]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = tab

View File

@@ -10,21 +10,23 @@ body:
## Welcome!
**在提交 Issue 之前,请确认以下事项**
1. 我**确认**已尝试过使用当前最新版本,并能复现问题。
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.
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 的具体版本。 / Please provide the specific version of Certimate.
description: 请提供 Certimate 的具体版本(请不要填写 `latest` 之类的无效版本号)。 / Please provide the specific version of Certimate.
placeholder: (e.g. v1.0.0)
validations:
required: true
@@ -70,8 +72,9 @@ body:
validations:
required: false
- type: markdown
- type: checkboxes
attributes:
value: |
请保持每个 Issue 只包含一个缺陷报告。
Please limit one report per issue.
label: 贡献 / Contribution
options:
- label: 我乐意为此贡献代码! / I am interested in contributing to this issue!
required: false

View File

@@ -14,12 +14,14 @@ body:
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:
@@ -38,7 +40,7 @@ body:
- type: textarea
attributes:
label: 其他 / Miscellaneous
description: 在此处添加关于该 Issue 的任何其他信息。 / Add any other context about the problem here.
description: 在此处添加关于该 Issue 的任何其他信息(新增提供商请求请补充 API 文档链接等资料)。 / Add any other context about the problem here.
validations:
required: false
@@ -46,11 +48,5 @@ body:
attributes:
label: 贡献 / Contribution
options:
- label: 我乐意为此贡献代码! / I am interested in contributing to this feature!
- label: 我乐意为此贡献代码! / I am interested in contributing to this issue!
required: false
- type: markdown
attributes:
value: |
请保持每个 Issue 只包含一个功能请求。
Please limit one request per issue.

View File

@@ -3,7 +3,7 @@ description: "遇到了困难需要求助? / Have problem in use and need help
title: "简要描述你遇到的问题"
body:
- type: markdown
attributes:
attributes:
value: |
## Welcome!
@@ -12,17 +12,19 @@ body:
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 的具体版本。 / Please provide the specific version of Certimate.
description: 请提供 Certimate 的具体版本(请不要填写 `latest` 之类的无效版本号)。 / Please provide the specific version of Certimate.
placeholder: (e.g. v1.0.0)
validations:
required: true
@@ -40,9 +42,3 @@ body:
description: 在此处添加关于该问题的任何其他信息。 / Add any other context about the problem here.
validations:
required: false
- type: markdown
attributes:
value: |
请保持每个 Issue 只包含一个问题求助。
Please limit one question per issue.

View File

@@ -1,4 +1,4 @@
name: Base Build
name: Release
on:
push:
@@ -22,13 +22,22 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ">=1.23.0"
go-version-file: "go.mod"
- 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
uses: goreleaser/goreleaser-action@v3
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: latest

View File

@@ -36,7 +36,7 @@ release:
archives:
- id: archive_noncgo
builds: [build_noncgo]
format: zip
format: "zip"
files:
- CHANGELOG.md
- LICENSE.md

View File

@@ -9,7 +9,7 @@
## 前提条件
- Go 1.22+ (用于修改 Go 代码)
- Go 1.24+ (用于修改 Go 代码)
- Node 20+ (用于修改 UI)
如果还没有这样做,你可以 fork Certimate 的主仓库,并克隆到本地以便进行修改:

View File

@@ -9,7 +9,7 @@ Thank you for taking the time to improve Certimate! Below is a guide for submitt
## Prerequisites
- Go 1.22+ (for Go code changes)
- Go 1.24+ (for Go code 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:

View File

@@ -8,7 +8,7 @@ RUN \
FROM golang:1.23-alpine AS builder
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY ../. /app/
RUN rm -rf /app/ui/dist

View File

@@ -38,8 +38,8 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
- 灵活的工作流编排方式,证书从申请到部署完全自动化;
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
- 支持 PEM、PFX、JKS 等多种格式输出证书;
- 支持 20+ 域名托管商如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers)
- 支持 70+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-host-providers)
- 支持 30+ 域名托管商如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers)
- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-hosting-providers)
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
- 更多特性等待探索。

View File

@@ -38,8 +38,8 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate
- Flexible workflow orchestration, fully automation from certificate application to deployment;
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
- 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 70+ 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 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 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-hosting-providers));
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust ServicesSSL.com, ZeroSSL, and more;
- More features waiting to be discovered.

195
go.mod
View File

@@ -1,89 +1,96 @@
module github.com/usual2970/certimate
go 1.23.0
go 1.24.0
toolchain go1.23.2
toolchain go1.24.3
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1
github.com/Edgio/edgio-api v0.0.0-workspace
github.com/G-Core/gcorelabscdn-go v1.0.28
github.com/G-Core/gcorelabscdn-go v1.0.31
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/cdn-20180510/v5 v5.2.2
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4
github.com/alibabacloud-go/esa-20240910/v2 v2.23.0
github.com/alibabacloud-go/fc-20230330/v4 v4.1.7
github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3
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/ga-20191120/v3 v3.1.8
github.com/alibabacloud-go/live-20161101 v1.1.1
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
github.com/alibabacloud-go/slb-20140515/v4 v4.0.10
github.com/alibabacloud-go/tea v1.3.4
github.com/alibabacloud-go/vod-20170321/v4 v4.7.0
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5
github.com/alibabacloud-go/tea v1.3.9
github.com/alibabacloud-go/vod-20170321/v4 v4.8.4
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2
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/cloudfront v1.45.2
github.com/baidubce/bce-sdk-go v0.9.221
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42
github.com/go-acme/lego/v4 v4.22.2
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1
github.com/baidubce/bce-sdk-go v0.9.226
github.com/blinkbean/dingtalk v1.1.3
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46
github.com/go-acme/lego/v4 v4.23.1
github.com/go-lark/lark v1.16.0
github.com/go-resty/resty/v2 v2.16.5
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148
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/nikoksr/notify v1.3.0
github.com/luthermonson/go-proxmox v0.2.2
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
github.com/pkg/sftp v1.13.8
github.com/pkg/sftp v1.13.9
github.com/pocketbase/dbx v1.11.0
github.com/pocketbase/pocketbase v0.26.1
github.com/povsister/scp v0.0.0-20240802064259-28781e87b246
github.com/qiniu/go-sdk/v7 v7.25.2
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115
github.com/pocketbase/pocketbase v0.28.0
github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c
github.com/qiniu/go-sdk/v7 v7.25.3
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125
github.com/ucloud/ucloud-sdk-go v0.22.31
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9
github.com/volcengine/volc-sdk-golang v1.0.199
github.com/volcengine/volcengine-go-sdk v1.0.187
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162
github.com/ucloud/ucloud-sdk-go v0.22.35
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12
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/ecloudsdkcore v1.0.0
golang.org/x/crypto v0.36.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.3
golang.org/x/crypto v0.38.0
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
k8s.io/api v0.33.0
k8s.io/apimachinery v0.33.0
k8s.io/client-go v0.33.0
software.sslmate.com/src/go-pkcs12 v0.5.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.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/privatedns/armprivatedns v1.3.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/openplatform-20191219/v2 v2.0.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-utils/v2 v2.0.7 // 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/blinkbean/dingtalk v1.1.3 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // 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/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-lark/lark v1.15.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
@@ -92,22 +99,23 @@ require (
github.com/go-playground/universal-translator v0.18.1 // 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/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/go-cmp v0.7.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/kylelemons/godebug v1.1.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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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/porkbun v0.4.0 // indirect
@@ -116,8 +124,7 @@ require (
github.com/qiniu/dyn v1.3.0 // indirect
github.com/qiniu/x v1.10.5 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/x448/float16 v0.8.4 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
@@ -125,15 +132,16 @@ require (
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // 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
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
)
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/dcdn-20180115/v3 v3.5.0
github.com/alibabacloud-go/debug v1.0.1 // indirect
@@ -141,35 +149,35 @@ require (
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-xml v1.1.3 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect
github.com/aliyun/credentials-go v1.4.3 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
github.com/aliyun/credentials-go v1.4.6 // 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/config v1.29.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.58
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.9
github.com/aws/aws-sdk-go-v2/credentials v1.17.62
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // 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/ini v1.8.2 // 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.13 // 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/aws-sdk-go-v2/internal/ini v1.8.3 // 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/presigned-url v1.12.15 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.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/disintegration/imaging v1.6.2 // indirect
github.com/domodwyer/mailyak/v3 v3.6.2
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/ganigeorgiev/fexpr v0.4.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/ganigeorgiev/fexpr v0.5.0 // 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/goccy/go-json v0.10.4 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -178,42 +186,39 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // 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/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/cast v1.8.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // 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
golang.org/x/image v0.25.0 // indirect
golang.org/x/image v0.27.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/sync v0.12.0
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.9.0
golang.org/x/tools v0.31.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.14.0
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.11.0
golang.org/x/tools v0.33.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.0 // 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/memory v1.8.2 // indirect
modernc.org/sqlite v1.36.1 // indirect
modernc.org/memory v1.9.1 // indirect
modernc.org/sqlite v1.37.0 // indirect
)
replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/vendors/edgio-sdk/edgio-api@v0.0.0-workspace
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/ecloudsdkcore v1.0.0 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkcore@v1.0.0
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/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1
replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./internal/pkg/sdk3rd/cmcc/ecloudsdkclouddns@v1.0.1

420
go.sum
View File

@@ -34,18 +34,14 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 h1:btEsytNrA4TG3edZnnUnzOz8W2MjOd6Bu3/7xyOXSOY=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0/go.mod h1:5SlTxxL1U4LLipEr7pAbnu6Ck5y3aIEu4L/tVbGmpsY=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
@@ -56,19 +52,23 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourceg
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 h1:HUJQzFYTv7t3V1dxPms52eEgl0l9xCNqutDrY45Lvmw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1/go.mod h1:ig/8nSkzmfxm5QGeIy5JYIEj8JEFy5JxvY3OB1YNRC4=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/G-Core/gcorelabscdn-go v1.0.28 h1:6ymVMV3HPTICO5BWJCEcZZzgY+Pc/+/TQMzeXMN77GQ=
github.com/G-Core/gcorelabscdn-go v1.0.28/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE=
github.com/G-Core/gcorelabscdn-go v1.0.31 h1:0ulvvM3yMDQ+Q5089tLsQxffL7I4pqf2bFCurU+DeHw=
github.com/G-Core/gcorelabscdn-go v1.0.31/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE=
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -95,10 +95,14 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/apig-20240327/v3 v3.2.2 h1:yH84ePgqtA2tF3ly7Tf3AA5ogl2SC8kqCNG4+zz4yo4=
github.com/alibabacloud-go/apig-20240327/v3 v3.2.2/go.mod h1:XLaCapbSH7olJTs42wisDO9JvX9BGy5acZk0bLNejDs=
github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 h1:ngRlctbt135zoujwX0lXSv9m4h1/bmg/yalQS0z1EWc=
github.com/alibabacloud-go/cas-20200407/v3 v3.0.4/go.mod h1:6n9MZ9SH3HlSzfe2oKwjOqhJx3dxvW2gMDO+lq8t9U4=
github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 h1:+KJOPukTM+xMyiLOW5qBwYKG2df3Ar7coRsqc1juKO8=
github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2/go.mod h1:GnPiPL3HlzCi8SGiLiVgKrAFkP1vTtcF4yGtjsl4wfo=
github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3 h1:OTLn0ShbE0jJj+5Z+P76zeHsZYxZjO7YVThQoeaBM9M=
github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3/go.mod h1:eUmD1G4BjEBOAHIeJrHJL7pyLGgXSRTPLjBmYY7uPEg=
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
@@ -108,15 +112,19 @@ github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+M
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.5/go.mod h1:kUe8JqFmoVU7lfBauaDD5taFaW7mBI+xVsyHutYtabg=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4 h1:IGSZHlOnWwBbLtX5xDplQvZOH0nkrV7Wmq+Fto7JK5w=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4/go.mod h1:Wxis0IBFusdbo44HO6KYYCJR1rRkoh47QQOYWvaheSU=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 h1:ASXSBga98QrGMxbIThCD6jAti09gedLfvry6yJtsoBE=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7/go.mod h1:TBpgqm3XofZz2LCYjZhektGPU7ArEgascyzbm4SjFo4=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0 h1:EQmKhYju6y38kJ1ZvZROeJG2Q1Wk6hlc8KQrVhvGyaw=
github.com/alibabacloud-go/dcdn-20180115/v3 v3.5.0/go.mod h1:b9qzvr/2V1f0r1Z6xUmkLqEouKcPGy4LCC22yV+6HQo=
github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0 h1:z9dPOvBRxzpD+FQ2uu/p2Z92I+PY9MUZMauwC+8AC6M=
github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0/go.mod h1:Cdg3Fu4jFByamRzt3AkeiBssoVPRNDs+EPYMP2fIj78=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
@@ -124,12 +132,14 @@ github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/ql
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8=
github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/esa-20240910/v2 v2.23.0 h1:Z/AALmxhOfzN+35tNCvm62/pET4IlxhDQn4nsdLqNzk=
github.com/alibabacloud-go/esa-20240910/v2 v2.23.0/go.mod h1:P1w/+i7dE2xSXVHJznEOVImlLtqqrzUJQQk2AsyBJ6o=
github.com/alibabacloud-go/fc-20230330/v4 v4.1.7 h1:rQvPfzPaouL/WGNgMDMCplA4wDscmVFff7aLCUkjv4g=
github.com/alibabacloud-go/fc-20230330/v4 v4.1.7/go.mod h1:ssEfKO6MskPtq7QaQnyiOHGWLXOZcl7a8YIf8u56DGc=
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 h1:eudSgNIkCg6huIu3HuF16BJG6+CA6bIuIddxpuPydpg=
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0/go.mod h1:HZS5PmYJvcmH4vrJYuCvK3AnYzD9hLlO8CT0hgRyDXo=
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGLph8j0ptEdZDPGBA=
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE=
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM=
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4=
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 h1:5GF0PXijDhxRQ3gTg9Ee/CVPtglkxuVdz4yIQgYLPgw=
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8/go.mod h1:RVpR9VL4YECKoZCQijTYfPk8k52O61v6hSRekjxF0kw=
github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8=
github.com/alibabacloud-go/live-20161101 v1.1.1/go.mod h1:g84w6qeAodT0/IHdc0tEed2a8PyhQhYl7TAj3jGl4A4=
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 h1:LtyUVlgBEKyzWgQJurzXM6MXCt84sQr9cE5OKqYymko=
@@ -153,13 +163,14 @@ github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.2/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.4 h1:QGTns2930y+ANmoNcUS74TgYpsoqusSrLIyYDOvIFFI=
github.com/alibabacloud-go/tea v1.3.4/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.8/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.9 h1:bjgt1bvdY780vz/17iWNNtbXl4A77HWntWMeaUF3So0=
github.com/alibabacloud-go/tea v1.3.9/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea-fileform v1.1.1 h1:1YG6erAP3joQ0XdCXYIotuD7zyOM6qCR49xkp5FZDeU=
github.com/alibabacloud-go/tea-fileform v1.1.1/go.mod h1:ZeCV91o4ISmxidd686f0ebdS5EDHWU+vW+TkjLhrsFE=
github.com/alibabacloud-go/tea-oss-sdk v1.1.3 h1:EhAHI6edMeqgkZEqP7r4nc9iMWAUBKGxJHoBsOSKTtU=
github.com/alibabacloud-go/tea-oss-sdk v1.1.3/go.mod h1:yUnodpR3Bf2rudLE7V/Gft5txjJF30Pk+hH77K/Eab0=
github.com/alibabacloud-go/tea-oss-sdk v1.1.5 h1:CFUFcqanvBaoGN/CyTHUZrVNtFZd1WTjem46m0HTTV0=
github.com/alibabacloud-go/tea-oss-sdk v1.1.5/go.mod h1:5fhlKMa/kWRJNgPYRt+5qSg3UidRvNbf9Z2bI8Dp5/s=
github.com/alibabacloud-go/tea-oss-utils v1.1.0 h1:y65crjjcZ2Pbb6UZtC2deuIZHDVTS3IaDWE7M9nVLRc=
github.com/alibabacloud-go/tea-oss-utils v1.1.0/go.mod h1:PFCF12e9yEKyBUIn7X1IrF/pNjvxgkHy0CgxX4+xRuY=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
@@ -177,20 +188,21 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alibabacloud-go/vod-20170321/v4 v4.7.0 h1:hpsnJBX5EeMrFujopMCjfq+p8XbNvPhFw6LOTV/WHd8=
github.com/alibabacloud-go/vod-20170321/v4 v4.7.0/go.mod h1:TkgLKMSLu0qZN8Qdcu8svfHREyI64kjFvrp/GhrD4VQ=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5 h1:ldAm1nvsCq66igjtcZyGhAoLClr+2eZ/pMIBUdKCOMM=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.5/go.mod h1:DohGoS8BnMxHXghHebtjPP7+GMdxPsRN19T3nn2HcCU=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 h1:YBkf7H5CSgrlb3C1aWcpDt7Vk8UEGFPeD2OOirtt6IM=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/alibabacloud-go/vod-20170321/v4 v4.8.4 h1:MYP2xfrcud8vlWljQ4lhemNgAgi9/AUAa450n8TUXZo=
github.com/alibabacloud-go/vod-20170321/v4 v4.8.4/go.mod h1:5ocQ6hIc9tpGixD2iy099aOGwIgpzjT2le4Krd4aLn8=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2 h1:CmhJzCZ5RiSiWU6BV2XJUtIMD2LDo9FFfqlYGtx1aAw=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2/go.mod h1:9itYSTzipL3NlvhvNYfTjFaapoZzG68nlu/KUdh9SpA=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.3 h1:N3iHyvHRMyOwY1+0qBLSf3hb5JFiOujVSVuEpgeGttY=
github.com/aliyun/credentials-go v1.4.3/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.6 h1:CG8rc/nxCNKfXbZWpWDzI9GjF4Tuu3Es14qT8Y0ClOk=
github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -206,40 +218,40 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.5 h1:4lS2IB+wwkj5J43Tq/AwvnscBerBJtQQ6YS7puzCI1k=
github.com/aws/aws-sdk-go-v2/config v1.29.5/go.mod h1:SNzldMlDVbN6nWxM7XsUiNXPSa1LWlqiXtvh/1PrJGg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58 h1:/d7FUpAPU8Lf2KUdjniQvfNdlMID0Sd9pS23FJ3SS9Y=
github.com/aws/aws-sdk-go-v2/credentials v1.17.58/go.mod h1:aVYW33Ow10CyMQGFgC0ptMRIqJWvJ4nxZb0sUiuQT/A=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI=
github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0=
github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U=
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU=
github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/acm v1.31.1 h1:FB1PgU6vlXbqehxZiHuYQRWo5Ou6sQrFJcUaRe27lRo=
github.com/aws/aws-sdk-go-v2/service/acm v1.31.1/go.mod h1:3sKYAgRbuBa2QMYGh/WEclwnmfx+QoPhhX25PdSQSQM=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.2 h1:S3JpsBLyn/jqSJ6GgsbDQHubmop6fshQk/iOaOeotsc=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.45.2/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0 h1:Ik/TAn4TBw/t3JhQJKtwjgoOf6kg5nXc190TiGhNrmI=
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0/go.mod h1:3sKYAgRbuBa2QMYGh/WEclwnmfx+QoPhhX25PdSQSQM=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1 h1:6xZNYtuVwzBs8k+TmraERt0vL68Ppg9aUi+aTQmPaVM=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1/go.mod h1:FIBJ48TS+qJb+Ne4qJ+0NeIhtPTVXItXooTeNeVI4Po=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 h1:njgAP7Rtt4DGdTGFPhJ4gaZXCD1CDj/SZDa5W4ZgSTs=
github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 h1:/nkJHXtJXJeelXHqG0898+fWKgvfaXBhGzbCsSmn9j8=
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/baidubce/bce-sdk-go v0.9.221 h1:x5uTXND33m5TE3UBXYhlePuXcJi5rxNnBBt+bP7kPe0=
github.com/baidubce/bce-sdk-go v0.9.221/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI=
github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -247,8 +259,10 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo6E4=
github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42 h1:Dm9FDjQP2SlAjUH7WAV7DFYDBFQz2uBxX6JULs0Sxs0=
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.42/go.mod h1:CIL/T2dxgbIA79os+wl0Fq0vCbADTZNIddV6PNYB6DY=
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46 h1:Z0TagIjBDaoCZuJANP/P8+z+ef8dStVCBPjMZcI/y8s=
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46/go.mod h1:CIL/T2dxgbIA79os+wl0Fq0vCbADTZNIddV6PNYB6DY=
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
@@ -271,8 +285,8 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA=
github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -290,6 +304,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/diskfs/go-diskfs v1.5.0 h1:0SANkrab4ifiZBytk380gIesYh5Gc+3i40l7qsrYP4s=
github.com/diskfs/go-diskfs v1.5.0/go.mod h1:bRFumZeGFCO8C2KNswrQeuj2m1WCVr4Ms5IjWMczMDk=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -300,6 +318,8 @@ github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -325,26 +345,26 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego/v4 v4.22.2 h1:ck+HllWrV/rZGeYohsKQ5iKNnU/WAZxwOdiu6cxky+0=
github.com/go-acme/lego/v4 v4.22.2/go.mod h1:E2FndyI3Ekv0usNJt46mFb9LVpV/XBYT+4E3tz02Tzo=
github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4=
github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-lark/lark v1.15.1 h1:fo6PQKBJht/71N9Zn3/xjknOYx0TmdVuP+VP8NrUCsI=
github.com/go-lark/lark v1.15.1/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-lark/lark v1.16.0 h1:U6BwkLM9wrZedSM7cIiMofganr8PCvJN+M75w2lf2Gg=
github.com/go-lark/lark v1.16.0/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -383,13 +403,13 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
@@ -399,8 +419,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -430,8 +450,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -453,14 +471,13 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -470,8 +487,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d h1:tx51Lf+wdE+aavqH8TcPJoCjTf4cE8hrMzROghCely0=
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -487,8 +504,14 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -522,8 +545,8 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 h1:8i57QAi5u+iPAYze92bkIvZoHiS0J45ndul5glr/NE8=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148 h1:PdWSbniKnPhKe1B19KUHW/9ahYbFH2EY6Iq6sxOnomo=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -539,14 +562,14 @@ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJk
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 h1:xZc/ZRcrOhDx9Ra9htu6ui2gUUttmLsXIqH61LcvY4U=
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI=
github.com/jinzhu/copier v0.3.4/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -562,13 +585,15 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
@@ -595,6 +620,10 @@ github.com/libdns/dynv6 v1.0.0/go.mod h1:65PL/bAlyH0J+0WGlOJYnMpoIuXcg/FmW4dTBYW
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/luthermonson/go-proxmox v0.2.2 h1:BZ7VEj302wxw2i/EwTcyEiBzQib8teocB2SSkLHyySY=
github.com/luthermonson/go-proxmox v0.2.2/go.mod h1:oyFgg2WwTEIF0rP6ppjiixOHa5ebK1p8OaRiFhvICBQ=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -615,8 +644,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
@@ -649,8 +678,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs=
github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM=
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI=
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8=
github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM=
github.com/nrdcg/desec v0.10.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs=
github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk=
@@ -687,7 +716,10 @@ github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -696,19 +728,21 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0=
github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.26.1 h1:0WBqIRKKPCqp+xHPVLB4fevkoT9HVlR4BSuNwAt5oJ0=
github.com/pocketbase/pocketbase v0.26.1/go.mod h1:t5y5pfnhrEg//RuSzSg0a926OLZ0oQj66jYs3BzDJwA=
github.com/pocketbase/pocketbase v0.28.0 h1:dnMHSO0wuYpKs6oP3X5buw1lY9ptd8zy1fTjN+Ae+mA=
github.com/pocketbase/pocketbase v0.28.0/go.mod h1:WE6xMM4+pxKIVNl4B1mcOEZXlDvPGl7cZ64TW2iXHdI=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/povsister/scp v0.0.0-20240802064259-28781e87b246 h1:c4D8BPWLOxxdaxQLfLKQXH2YXY/E9yo3jrDSL54XrTw=
github.com/povsister/scp v0.0.0-20240802064259-28781e87b246/go.mod h1:i1Au86ZXK0ZalQNyBp2njCcyhSCR/QP/AMfILip+zNI=
github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c h1:1+j5JHz9mUzYSp0scuF6hzvJP28EDBFe5eBJb0xnGk4=
github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c/go.mod h1:CiJNEeV6v0tUCNul/+gTjl+FgjfImoiuptJB9AEzqjE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
@@ -731,22 +765,22 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/qiniu/dyn v1.3.0 h1:s+xPTeV0H8yikgM4ZMBc7Rrefam8UNI3asBlkaOQg5o=
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
github.com/qiniu/go-sdk/v7 v7.25.2 h1:URwgZpxySdiwu2yQpHk93X4LXWHyFRp1x3Vmlk/YWvo=
github.com/qiniu/go-sdk/v7 v7.25.2/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o=
github.com/qiniu/go-sdk/v7 v7.25.3 h1:eYHh02q4i5MrlEn3qy823w7moieymFzb4dsP38Y43AI=
github.com/qiniu/go-sdk/v7 v7.25.3/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o=
github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0=
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -758,15 +792,15 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0=
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
@@ -793,34 +827,33 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115 h1:HsrXyqKQB2mKfGq+ZkbylRCMrbtPCmmUBrwA8MhhEX0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1115/go.mod h1:5cz1DtLlXK98U1Hh36oW4PjVOU+mbKg5wtCDmCc9Fcs=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127 h1:PiLZflqaW0690YsqIM/hqaVYjZJ3+cCJp4NHfw7h/uw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1127/go.mod h1:V1+julLUOH0jKoVH6o6xgM4STWowzAL57M4VanUEEag=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1117/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155 h1:ildxJtjnqiKZxWDVKHT/ncIknGDijtg60MuFELON8bY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155/go.mod h1:iLASpooTdyXtx642E5Ws7cfWENsp4/uZ/78TFoln7OI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 h1:yGFg9/6j3NP10r9PfSWHfekuq4SwPyqblWnfISfKANo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161/go.mod h1:9MzQSEULYm5wHAKz8R3oQ8ovg4vWeLFzn0DmRWTc6zg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1120/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1124/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1125/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1126/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127 h1:1uG8zc0b9gLbyTr27T0CzGtcdrL86CGGJ6Flkq867f4=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1127/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 h1:kwctN0WQYt8/iKP+iRCTCwdzEMIXsXklbRIib5rjeQ8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084/go.mod h1:qE67ApiBzeRvzeDsV+GxyIDbVIDemsKpHXllQATz/Vw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117 h1:Yc/r5zUAyukVI3huIuwE7koowCjDjOWqeRpBILCvOSE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1117/go.mod h1:YxsorHl3sTRw+2GsUObMqcumDqAQ3zo9rLMtf3Cxj8U=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115 h1:Qi7VWmJ0AQxEMlwKpbWfnsLA5QdNxekdcLJTBVdO85U=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1115/go.mod h1:P16nIMvmpSY+arTc2m2HyJmrYQP6CFnr48glz0+abyw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1155/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1160/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1161/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 h1:bscCBygP9JRl6iNabF+vmBOhY+xayFFGYV5Wa0NzH0A=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150 h1:RQQYfZOFYlkxKR2+xp8el3+8xs9DhxBy+ajlHtapqtQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150/go.mod h1:zpfr6EBWy7ClASTGUgIy01Gn4R79UXf+2QGQeyR124A=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 h1:z0t0lb5h1mZirXftO8MRg25COYZHx0ubQjSPhZT/LY0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120/go.mod h1:IFZL44Keyl+MHrhpFwUaQmJvMDwGr+t+cUfFAC+74lU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 h1:LQKAlxFb0sYiE8ojK5h9+seuFzogoJtYnXmiRF+4F4Q=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124/go.mod h1:tYbK0FbHVG+78od7eZpzczE8qk0JWKO/osTQWuiJ3Fo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126 h1:+CJQNXLdLP0GLaz2fnPECQsU+WdOmW3BQ54cNoQgMKA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1126/go.mod h1:eY3GoWilNoCPOEw2Lp4o+h02nEfc+BoZnqK3TlK6F7I=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115 h1:rO0LdbcNtT5VlL9sB/K3Ve848uLp1rgg3R8igT9xsFQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1115/go.mod h1:jJR8Y5sHuujSXZy0cpCgBk180TvPNsLw9hEoSH9w7iA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125 h1:IR9pJqHjHr7KyncRVxld9iltfnmy9sCC+0USZrs3rOw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1125/go.mod h1:5+5QrF7x+AW1KPM7F+YRzD74L88RXHZ6BxDF07b8QkE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162 h1:z/JF+JGi6bGf8vnK9ZeVXz+1Q3ih8nF6KjThxhtIrNc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162/go.mod h1:rsO8JCP+WQeLQ32wAB4oRRjsEz0O+kvCGDqc7Ze1jc4=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160 h1:aNVEDS1yQ7sLfXOOQ/bF3eljFjyvHoJ/J8qSC9mC9gw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160/go.mod h1:kf6NQmKK6sh1ACwh8iliBy7I/burd+AWusNz6zbDvLM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162 h1:gnmuUaoFAShc9FKj3Omswu3n08bHM/sGsl8xjFAkFNs=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162/go.mod h1:bu3KAFeoJ1xDGQp72h9Le3FqbOcCcdomOUig3OqgcE4=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
@@ -829,16 +862,18 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ucloud/ucloud-sdk-go v0.22.31 h1:izZK+Re9ZkJAd1fHSVpFzgh8uKda4f5G6++iUw4n/mE=
github.com/ucloud/ucloud-sdk-go v0.22.31/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
github.com/ucloud/ucloud-sdk-go v0.22.35 h1:Q4CY3Ae5813jmNUrGdCjc8tlyleL5Lyl0APnpK5L4sk=
github.com/ucloud/ucloud-sdk-go v0.22.35/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9 h1:fEnScn2dXfvfNcFnvJnpf/cYdj8kLIe5QC5qORlFO2c=
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.9/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM=
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12 h1:u9+32DXQIOFPG8oQ3xrjSAUSyAcaq5bqO4cEBom/6lA=
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM=
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi+pjq8ILWjLYUc=
github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/volcengine/volcengine-go-sdk v1.0.187 h1:YpZjydoyHDA/ofnF6mYCelbOoo9pJsBEiQOOSJzGSOY=
github.com/volcengine/volcengine-go-sdk v1.0.187/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA=
github.com/volcengine/volc-sdk-golang v1.0.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18=
github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/volcengine/volcengine-go-sdk v1.1.7 h1:5ElF1inqX1QUKX8/XGk+HGpG+F01W+m73cLQH+0x50s=
github.com/volcengine/volcengine-go-sdk v1.1.7/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -906,8 +941,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -921,14 +956,14 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1011,16 +1046,16 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1038,8 +1073,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1092,6 +1127,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1100,6 +1136,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1116,8 +1153,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1133,8 +1170,8 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1153,15 +1190,15 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1216,8 +1253,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1357,39 +1394,39 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ=
modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
@@ -1400,8 +1437,11 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@@ -1,30 +1,31 @@
package applicant
package applicant
import "github.com/usual2970/certimate/internal/domain"
const (
sslProviderLetsEncrypt = string(domain.ApplyCAProviderTypeLetsEncrypt)
sslProviderLetsEncryptStaging = string(domain.ApplyCAProviderTypeLetsEncryptStaging)
sslProviderBuypass = string(domain.ApplyCAProviderTypeBuypass)
sslProviderGoogleTrustServices = string(domain.ApplyCAProviderTypeGoogleTrustServices)
sslProviderSSLCom = string(domain.ApplyCAProviderTypeSSLCom)
sslProviderZeroSSL = string(domain.ApplyCAProviderTypeZeroSSL)
caLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
caLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
caBuypass = string(domain.CAProviderTypeBuypass)
caGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
caSSLCom = string(domain.CAProviderTypeSSLCom)
caZeroSSL = string(domain.CAProviderTypeZeroSSL)
caCustom = string(domain.CAProviderTypeACMECA)
sslProviderDefault = sslProviderLetsEncrypt
caDefault = caLetsEncrypt
)
var sslProviderUrls = map[string]string{
sslProviderLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
sslProviderLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
sslProviderBuypass: "https://api.buypass.com/acme/directory",
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",
var caDirUrls = map[string]string{
caLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
caLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
caBuypass: "https://api.buypass.com/acme/directory",
caGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory",
caSSLCom: "https://acme.ssl.com/sslcom-dv-rsa",
caSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa",
caSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc",
caZeroSSL: "https://acme.zerossl.com/v2/DV90",
}
type acmeSSLProviderConfig struct {
Config map[domain.ApplyCAProviderType]map[string]any `json:"config"`
Provider string `json:"provider"`
Config map[domain.CAProviderType]map[string]any `json:"config"`
Provider string `json:"provider"`
}

View File

@@ -1,4 +1,4 @@
package applicant
package applicant
import (
"context"
@@ -7,34 +7,44 @@ import (
"crypto/elliptic"
"crypto/rand"
"fmt"
"strings"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"golang.org/x/sync/singleflight"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
"github.com/usual2970/certimate/internal/repository"
)
type acmeUser struct {
CA string
Email string
// 证书颁发机构标识。
// 通常等同于 [CAProviderType] 的值。
// 对于自定义 ACME CA值为 "custom#{access_id}"。
CA string
// 邮箱。
Email string
// 注册信息。
Registration *registration.Resource
// CSR 私钥。
privkey string
}
func newAcmeUser(ca, email string) (*acmeUser, error) {
func newAcmeUser(ca, caAccessId, email string) (*acmeUser, error) {
repo := repository.NewAcmeAccountRepository()
applyUser := &acmeUser{
CA: ca,
Email: email,
}
if ca == caCustom {
applyUser.CA = fmt.Sprintf("%s#%s", ca, caAccessId)
}
acmeAccount, err := repo.GetByCAAndEmail(ca, email)
acmeAccount, err := repo.GetByCAAndEmail(applyUser.CA, applyUser.Email)
if err != nil {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
@@ -73,6 +83,10 @@ func (u *acmeUser) hasRegistration() bool {
return u.Registration != nil
}
func (u *acmeUser) getCAProvider() string {
return strings.Split(u.CA, "#")[0]
}
func (u *acmeUser) getPrivateKeyPEM() string {
return u.privkey
}
@@ -94,16 +108,16 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userR
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
var reg *registration.Resource
var err error
switch user.CA {
case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
switch user.getCAProvider() {
case caLetsEncrypt, caLetsEncryptStaging:
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
case sslProviderBuypass:
case caBuypass:
{
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
case sslProviderGoogleTrustServices:
case caGoogleTrustServices:
{
access := domain.AccessConfigForGoogleTrustServices{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -117,7 +131,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
case sslProviderSSLCom:
case caSSLCom:
{
access := domain.AccessConfigForSSLCom{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -131,7 +145,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
case sslProviderZeroSSL:
case caZeroSSL:
{
access := domain.AccessConfigForZeroSSL{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
@@ -145,8 +159,28 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
})
}
case caCustom:
{
access := domain.AccessConfigForACMECA{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
if access.EabKid == "" && access.EabHmacKey == "" {
reg, err = client.Registration.Register(registration.RegisterOptions{
TermsOfServiceAgreed: true,
})
} else {
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: access.EabKid,
HmacEncoded: access.EabHmacKey,
})
}
}
default:
err = fmt.Errorf("unsupported ca provider: %s", user.CA)
err = fmt.Errorf("unsupported ca provider '%s'", user.CA)
}
if err != nil {
return nil, err

View File

@@ -4,10 +4,12 @@ import (
"context"
"encoding/json"
"fmt"
"log/slog"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
@@ -18,12 +20,13 @@ import (
"golang.org/x/time/rate"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
"github.com/usual2970/certimate/internal/repository"
)
type ApplyCertResult struct {
CertificateFullChain string
type ApplyResult struct {
FullChainCertificate string
IssuerCertificate string
PrivateKey string
ACMEAccountUrl string
@@ -33,47 +36,38 @@ type ApplyCertResult struct {
}
type Applicant interface {
Apply() (*ApplyCertResult, error)
Apply(ctx context.Context) (*ApplyResult, error)
}
type applicantOptions struct {
Domains []string
ContactEmail string
Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any
ProviderExtendedConfig map[string]any
CAProvider domain.ApplyCAProviderType
CAProviderAccessConfig map[string]any
CAProviderExtendedConfig map[string]any
KeyAlgorithm string
Nameservers []string
DnsPropagationTimeout int32
DnsTTL int32
DisableFollowCNAME bool
ReplacedARIAcct string
ReplacedARICert string
type ApplicantWithWorkflowNodeConfig struct {
Node *domain.WorkflowNode
Logger *slog.Logger
}
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
if node.Type != domain.WorkflowNodeTypeApply {
return nil, fmt.Errorf("node type is not apply")
func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, error) {
if config.Node == nil {
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()
options := &applicantOptions{
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
ContactEmail: nodeConfig.ContactEmail,
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderExtendedConfig: nodeConfig.ProviderConfig,
CAProvider: domain.ApplyCAProviderType(nodeConfig.CAProvider),
CAProviderAccessConfig: make(map[string]any),
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
KeyAlgorithm: nodeConfig.KeyAlgorithm,
Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
DnsTTL: nodeConfig.DnsTTL,
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
nodeConfig := config.Node.GetConfigForApply()
options := &applicantProviderOptions{
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
ContactEmail: nodeConfig.ContactEmail,
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderServiceConfig: nodeConfig.ProviderConfig,
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
CAProviderAccessConfig: make(map[string]any),
CAProviderServiceConfig: nodeConfig.CAProviderConfig,
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()
@@ -88,6 +82,7 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
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.CAProviderAccessId = access.Id
options.CAProviderAccessConfig = access.Config
}
}
@@ -97,23 +92,23 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
sslProviderConfig := &acmeSSLProviderConfig{
Config: make(map[domain.ApplyCAProviderType]map[string]any),
Provider: sslProviderDefault,
Config: make(map[domain.CAProviderType]map[string]any),
Provider: caDefault,
}
if settings != nil {
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
return nil, err
} else if sslProviderConfig.Provider == "" {
sslProviderConfig.Provider = sslProviderDefault
sslProviderConfig.Provider = caDefault
}
}
options.CAProvider = domain.ApplyCAProviderType(sslProviderConfig.Provider)
options.CAProvider = domain.CAProviderType(sslProviderConfig.Provider)
options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider]
}
certRepo := repository.NewCertificateRepository()
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), node.Id)
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id)
if lastCertificate != nil {
newCertSan := slices.Clone(options.Domains)
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
@@ -130,19 +125,47 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
}
}
applicant, err := createApplicant(options)
applicant, err := createApplicantProvider(options)
if err != nil {
return nil, err
}
return &proxyApplicant{
return &applicantImpl{
applicant: applicant,
options: options,
}, nil
}
func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) {
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
type applicantImpl struct {
applicant challenge.Provider
options *applicantProviderOptions
}
var _ Applicant = (*applicantImpl)(nil)
func (d *applicantImpl) Apply(ctx context.Context) (*ApplyResult, error) {
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
if err := limiter.Wait(ctx); err != nil {
return nil, err
}
return applyUseLego(d.applicant, d.options)
}
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.CAProviderAccessId, options.ContactEmail)
if err != nil {
return nil, err
}
@@ -153,14 +176,27 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
// Create an ACME client config
config := lego.NewConfig(user)
config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
config.CADirURL = sslProviderUrls[user.CA]
if user.CA == sslProviderSSLCom {
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
switch user.getCAProvider() {
case caSSLCom:
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"]
config.CADirURL = caDirUrls[caSSLCom+"RSA"]
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"]
config.CADirURL = caDirUrls[caSSLCom+"ECC"]
} else {
config.CADirURL = caDirUrls[caSSLCom]
}
case caCustom:
caDirURL := maputil.GetString(options.CAProviderAccessConfig, "endpoint")
if caDirURL != "" {
config.CADirURL = caDirURL
} else {
return nil, fmt.Errorf("invalid ca provider endpoint")
}
default:
config.CADirURL = caDirUrls[user.CA]
}
// Create an ACME client
@@ -170,18 +206,26 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
}
// Set the DNS01 challenge provider
challengeOptions := make([]dns01.ChallengeOption, 0)
if len(options.Nameservers) > 0 {
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
}
client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...)
client.Challenge.SetDNS01Provider(legoProvider,
dns01.CondOption(
len(options.Nameservers) > 0,
dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)),
),
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
if !user.hasRegistration() {
reg, err := registerAcmeUserWithSingleFlight(client, user, options.CAProviderAccessConfig)
if err != nil {
return nil, fmt.Errorf("failed to register: %w", err)
return nil, fmt.Errorf("failed to register acme user: %w", err)
}
user.Registration = reg
}
@@ -199,8 +243,8 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
return nil, err
}
return &ApplyCertResult{
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
return &ApplyResult{
FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)),
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
ACMEAccountUrl: user.Registration.URI,
@@ -210,47 +254,20 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
}, nil
}
func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
switch algo {
case domain.CertificateKeyAlgorithmTypeRSA2048:
return certcrypto.RSA2048
case domain.CertificateKeyAlgorithmTypeRSA3072:
return certcrypto.RSA3072
case domain.CertificateKeyAlgorithmTypeRSA4096:
return certcrypto.RSA4096
case domain.CertificateKeyAlgorithmTypeRSA8192:
return certcrypto.RSA8192
case domain.CertificateKeyAlgorithmTypeEC256:
return certcrypto.EC256
case domain.CertificateKeyAlgorithmTypeEC384:
return certcrypto.EC384
case domain.CertificateKeyAlgorithmTypeEC512:
return certcrypto.KeyType("P512")
func parseLegoKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
alogMap := map[domain.CertificateKeyAlgorithmType]certcrypto.KeyType{
domain.CertificateKeyAlgorithmTypeRSA2048: certcrypto.RSA2048,
domain.CertificateKeyAlgorithmTypeRSA3072: certcrypto.RSA3072,
domain.CertificateKeyAlgorithmTypeRSA4096: certcrypto.RSA4096,
domain.CertificateKeyAlgorithmTypeRSA8192: certcrypto.RSA8192,
domain.CertificateKeyAlgorithmTypeEC256: certcrypto.EC256,
domain.CertificateKeyAlgorithmTypeEC384: certcrypto.EC384,
domain.CertificateKeyAlgorithmTypeEC512: certcrypto.KeyType("P512"),
}
if keyType, ok := alogMap[algo]; ok {
return keyType
}
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)
}

View File

@@ -8,9 +8,11 @@ import (
"github.com/usual2970/certimate/internal/domain"
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"
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"
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"
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"
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"
@@ -25,6 +27,8 @@ import (
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"
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"
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"
@@ -34,16 +38,36 @@ import (
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"
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
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
ProviderServiceConfig map[string]any
CAProvider domain.CAProviderType
CAProviderAccessId string
CAProviderAccessConfig map[string]any
CAProviderServiceConfig 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 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
switch options.Provider {
case domain.ApplyDNSProviderTypeACMEHttpReq:
case domain.ACMEDns01ProviderTypeACMEHttpReq:
{
access := domain.AccessConfigForACMEHttpReq{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -60,23 +84,39 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS:
case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS, domain.ACMEDns01ProviderTypeAliyunESA:
{
access := domain.AccessConfigForAliyun{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
switch options.Provider {
case domain.ACMEDns01ProviderTypeAliyun, domain.ACMEDns01ProviderTypeAliyunDNS:
applicant, err := pAliyun.NewChallengeProvider(&pAliyun.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
DnsPropagationTimeout: options.DnsPropagationTimeout,
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.ProviderServiceConfig, "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{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -86,15 +126,15 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"),
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
HostedZoneId: maputil.GetString(options.ProviderServiceConfig, "hostedZoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
case domain.ACMEDns01ProviderTypeAzure, domain.ACMEDns01ProviderTypeAzureDNS:
{
access := domain.AccessConfigForAzure{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -112,7 +152,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS:
case domain.ACMEDns01ProviderTypeBaiduCloud, domain.ACMEDns01ProviderTypeBaiduCloudDNS:
{
access := domain.AccessConfigForBaiduCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -128,7 +168,22 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeCloudflare:
case domain.ACMEDns01ProviderTypeBunny:
{
access := domain.AccessConfigForBunny{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pBunny.NewChallengeProvider(&pBunny.ChallengeProviderConfig{
ApiKey: access.ApiKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ACMEDns01ProviderTypeCloudflare:
{
access := domain.AccessConfigForCloudflare{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -144,7 +199,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeClouDNS:
case domain.ACMEDns01ProviderTypeClouDNS:
{
access := domain.AccessConfigForClouDNS{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -160,7 +215,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeCMCCCloud:
case domain.ACMEDns01ProviderTypeCMCCCloud:
{
access := domain.AccessConfigForCMCCCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -176,7 +231,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeDeSEC:
case domain.ACMEDns01ProviderTypeDeSEC:
{
access := domain.AccessConfigForDeSEC{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -191,7 +246,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeDNSLA:
case domain.ACMEDns01ProviderTypeDNSLA:
{
access := domain.AccessConfigForDNSLA{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -207,7 +262,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeDynv6:
case domain.ACMEDns01ProviderTypeDynv6:
{
access := domain.AccessConfigForDynv6{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -222,7 +277,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeGcore:
case domain.ACMEDns01ProviderTypeGcore:
{
access := domain.AccessConfigForGcore{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -237,7 +292,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeGname:
case domain.ACMEDns01ProviderTypeGname:
{
access := domain.AccessConfigForGname{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -253,7 +308,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeGoDaddy:
case domain.ACMEDns01ProviderTypeGoDaddy:
{
access := domain.AccessConfigForGoDaddy{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -269,7 +324,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS:
case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS:
{
access := domain.AccessConfigForHuaweiCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -279,14 +334,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
case domain.ACMEDns01ProviderTypeJDCloud, domain.ACMEDns01ProviderTypeJDCloudDNS:
{
access := domain.AccessConfigForJDCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -296,14 +351,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"),
RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeNamecheap:
case domain.ACMEDns01ProviderTypeNamecheap:
{
access := domain.AccessConfigForNamecheap{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -319,7 +374,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeNameDotCom:
case domain.ACMEDns01ProviderTypeNameDotCom:
{
access := domain.AccessConfigForNameDotCom{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -335,7 +390,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeNameSilo:
case domain.ACMEDns01ProviderTypeNameSilo:
{
access := domain.AccessConfigForNameSilo{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -350,7 +405,39 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
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{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -365,7 +452,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypePorkbun:
case domain.ACMEDns01ProviderTypePorkbun:
{
access := domain.AccessConfigForPorkbun{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -381,7 +468,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypePowerDNS:
case domain.ACMEDns01ProviderTypePowerDNS:
{
access := domain.AccessConfigForPowerDNS{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -389,15 +476,16 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
}
applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{
ApiUrl: access.ApiUrl,
ApiKey: access.ApiKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
ApiUrl: access.ApiUrl,
ApiKey: access.ApiKey,
AllowInsecureConnections: access.AllowInsecureConnections,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeRainYun:
case domain.ACMEDns01ProviderTypeRainYun:
{
access := domain.AccessConfigForRainYun{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -412,7 +500,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS, domain.ApplyDNSProviderTypeTencentCloudEO:
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS, domain.ACMEDns01ProviderTypeTencentCloudEO:
{
access := domain.AccessConfigForTencentCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -420,7 +508,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
}
switch options.Provider {
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
case domain.ACMEDns01ProviderTypeTencentCloud, domain.ACMEDns01ProviderTypeTencentCloudDNS:
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
@@ -429,11 +517,11 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
})
return applicant, err
case domain.ApplyDNSProviderTypeTencentCloudEO:
case domain.ACMEDns01ProviderTypeTencentCloudEO:
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -444,7 +532,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
}
}
case domain.ApplyDNSProviderTypeVercel:
case domain.ACMEDns01ProviderTypeVercel:
{
access := domain.AccessConfigForVercel{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -460,7 +548,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS:
case domain.ACMEDns01ProviderTypeVolcEngine, domain.ACMEDns01ProviderTypeVolcEngineDNS:
{
access := domain.AccessConfigForVolcEngine{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -476,7 +564,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeWestcn:
case domain.ACMEDns01ProviderTypeWestcn:
{
access := domain.AccessConfigForWestcn{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -493,5 +581,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))
}

View File

@@ -17,7 +17,7 @@ import (
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/domain/dtos"
"github.com/usual2970/certimate/internal/notify"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
"github.com/usual2970/certimate/internal/repository"
)

View File

@@ -11,31 +11,29 @@ import (
)
type Deployer interface {
SetLogger(*slog.Logger)
Deploy(ctx context.Context) error
}
type deployerOptions struct {
Provider domain.DeployProviderType
ProviderAccessConfig map[string]any
ProviderDeployConfig map[string]any
type DeployerWithWorkflowNodeConfig struct {
Node *domain.WorkflowNode
Logger *slog.Logger
CertificatePEM string
PrivateKeyPEM string
}
func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
Certificate string
PrivateKey string
},
) (Deployer, error) {
if node.Type != domain.WorkflowNodeTypeDeploy {
return nil, fmt.Errorf("node type is not deploy")
func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error) {
if config.Node == nil {
return nil, fmt.Errorf("node is nil")
}
if config.Node.Type != domain.WorkflowNodeTypeDeploy {
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeDeploy))
}
nodeConfig := node.GetConfigForDeploy()
options := &deployerOptions{
Provider: domain.DeployProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderDeployConfig: nodeConfig.ProviderConfig,
nodeConfig := config.Node.GetConfigForDeploy()
options := &deployerProviderOptions{
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderServiceConfig: nodeConfig.ProviderConfig,
}
accessRepo := repository.NewAccessRepository()
@@ -48,34 +46,27 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
}
}
deployer, err := createDeployer(options)
deployerProvider, err := createDeployerProvider(options)
if err != nil {
return nil, err
}
return &proxyDeployer{
deployer: deployer,
deployCertificate: certdata.Certificate,
deployPrivateKey: certdata.PrivateKey,
return &deployerImpl{
provider: deployerProvider.WithLogger(config.Logger),
certPEM: config.CertificatePEM,
privkeyPEM: config.PrivateKeyPEM,
}, nil
}
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
type proxyDeployer struct {
deployer deployer.Deployer
deployCertificate string
deployPrivateKey string
type deployerImpl struct {
provider deployer.Deployer
certPEM string
privkeyPEM string
}
func (d *proxyDeployer) SetLogger(logger *slog.Logger) {
if logger == nil {
panic("logger is nil")
}
var _ Deployer = (*deployerImpl)(nil)
d.deployer.WithLogger(logger)
}
func (d *proxyDeployer) Deploy(ctx context.Context) error {
_, 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
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -11,15 +11,23 @@ type Access struct {
Name string `json:"name" db:"name"`
Provider string `json:"provider" db:"provider"`
Config map[string]any `json:"config" db:"config"`
Reserve string `json:"reserve,omitempty" db:"reserve"`
DeletedAt *time.Time `json:"deleted" db:"deleted"`
}
type AccessConfigFor1Panel struct {
ApiUrl string `json:"apiUrl"`
ApiVersion string `json:"apiVersion"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForACMECA struct {
Endpoint string `json:"endpoint"`
EabKid string `json:"eabKid,omitempty"`
EabHmacKey string `json:"eabHmacKey,omitempty"`
}
type AccessConfigForACMEHttpReq struct {
Endpoint string `json:"endpoint"`
Mode string `json:"mode,omitempty"`
@@ -59,19 +67,30 @@ type AccessConfigForBaotaPanel struct {
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForBaotaWAF struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForBytePlus struct {
AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"`
}
type AccessConfigForBunny struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForCacheFly struct {
ApiToken string `json:"apiToken"`
}
type AccessConfigForCdnfly struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"`
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForCloudflare struct {
@@ -93,6 +112,11 @@ type AccessConfigForDeSEC struct {
Token string `json:"token"`
}
type AccessConfigForDingTalkBot struct {
WebhookUrl string `json:"webhookUrl"`
Secret string `json:"secret"`
}
type AccessConfigForDNSLA struct {
ApiId string `json:"apiId"`
ApiSecret string `json:"apiSecret"`
@@ -112,6 +136,24 @@ type AccessConfigForEdgio struct {
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 AccessConfigForFlexCDN struct {
ApiUrl string `json:"apiUrl"`
ApiRole string `json:"apiRole"`
AccessKeyId string `json:"accessKeyId"`
AccessKey string `json:"accessKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForGcore struct {
ApiToken string `json:"apiToken"`
}
@@ -126,6 +168,14 @@ type AccessConfigForGoDaddy struct {
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"`
@@ -145,6 +195,26 @@ type AccessConfigForKubernetes struct {
KubeConfig string `json:"kubeConfig,omitempty"`
}
type AccessConfigForLarkBot struct {
WebhookUrl string `json:"webhookUrl"`
}
type AccessConfigForLeCDN struct {
ApiUrl string `json:"apiUrl"`
ApiVersion string `json:"apiVersion"`
ApiRole string `json:"apiRole"`
Username string `json:"username"`
Password string `json:"password"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForMattermost struct {
ServerUrl string `json:"serverUrl"`
Username string `json:"username"`
Password string `json:"password"`
DefaultChannelId string `json:"defaultChannelId,omitempty"`
}
type AccessConfigForNamecheap struct {
Username string `json:"username"`
ApiKey string `json:"apiKey"`
@@ -159,6 +229,16 @@ type AccessConfigForNameSilo struct {
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 {
ApiKey string `json:"apiKey"`
}
@@ -169,8 +249,16 @@ type AccessConfigForPorkbun struct {
}
type AccessConfigForPowerDNS struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
ApiUrl string `json:"apiUrl"`
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 {
@@ -182,6 +270,13 @@ type AccessConfigForRainYun struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForRatPanel struct {
ApiUrl string `json:"apiUrl"`
AccessTokenId int32 `json:"accessTokenId"`
AccessToken string `json:"accessToken"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForSafeLine struct {
ApiUrl string `json:"apiUrl"`
ApiToken string `json:"apiToken"`
@@ -195,6 +290,14 @@ type AccessConfigForSSH struct {
Password string `json:"password,omitempty"`
Key string `json:"key,omitempty"`
KeyPassphrase string `json:"keyPassphrase,omitempty"`
JumpServers []struct {
Host string `json:"host"`
Port int32 `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
Key string `json:"key,omitempty"`
KeyPassphrase string `json:"keyPassphrase,omitempty"`
} `json:"jumpServers,omitempty"`
}
type AccessConfigForSSLCom struct {
@@ -202,6 +305,11 @@ type AccessConfigForSSLCom struct {
EabHmacKey string `json:"eabHmacKey"`
}
type AccessConfigForTelegramBot struct {
BotToken string `json:"botToken"`
DefaultChatId int64 `json:"defaultChatId,omitempty"`
}
type AccessConfigForTencentCloud struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
@@ -231,11 +339,20 @@ type AccessConfigForVolcEngine struct {
type AccessConfigForWangsu struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
ApiKey string `json:"apiKey"`
}
type AccessConfigForWebhook struct {
Url string `json:"url"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
Url string `json:"url"`
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 {

View File

@@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
)
const CollectionNameCertificate = "certificate"
@@ -20,7 +20,7 @@ type Certificate struct {
SerialNumber string `json:"serialNumber" db:"serialNumber"`
Certificate string `json:"certificate" db:"certificate"`
PrivateKey string `json:"privateKey" db:"privateKey"`
Issuer string `json:"issuer" db:"issuer"`
IssuerOrg string `json:"issuerOrg" db:"issuerOrg"`
IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"`
KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"`
EffectAt time.Time `json:"effectAt" db:"effectAt"`
@@ -38,7 +38,7 @@ type Certificate struct {
func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate {
c.SubjectAltNames = strings.Join(certX509.DNSNames, ";")
c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16))
c.Issuer = strings.Join(certX509.Issuer.Organization, ";")
c.IssuerOrg = strings.Join(certX509.Issuer.Organization, ";")
c.EffectAt = certX509.NotBefore
c.ExpireAt = certX509.NotAfter

View File

@@ -1,4 +1,4 @@
package dtos
package dtos
type CertificateArchiveFileReq struct {
CertificateId string `json:"-"`

View File

@@ -1,4 +1,4 @@
package dtos
package dtos
import "github.com/usual2970/certimate/internal/domain"

View File

@@ -1,4 +1,4 @@
package dtos
package dtos
import "github.com/usual2970/certimate/internal/domain"

View File

@@ -8,6 +8,7 @@ type NotifyChannelType string
注意:如果追加新的常量值,请保持以 ASCII 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
// Deprecated: v0.4.x 将废弃
const (
NotifyChannelTypeBark = NotifyChannelType("bark")
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")

View File

@@ -1,4 +1,4 @@
package domain
package domain
type AccessProviderType string
@@ -10,6 +10,7 @@ type AccessProviderType string
*/
const (
AccessProviderType1Panel = AccessProviderType("1panel")
AccessProviderTypeACMECA = AccessProviderType("acmeca")
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai预留
AccessProviderTypeAliyun = AccessProviderType("aliyun")
@@ -18,44 +19,57 @@ const (
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeBunny = AccessProviderType("bunny")
AccessProviderTypeBuypass = AccessProviderType("buypass")
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留)
AccessProviderTypeDeSEC = AccessProviderType("desec")
AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot")
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
AccessProviderTypeEdgio = AccessProviderType("edgio")
AccessProviderTypeEmail = AccessProviderType("email")
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly预留
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn")
AccessProviderTypeGname = AccessProviderType("gname")
AccessProviderTypeGcore = AccessProviderType("gcore")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge预留
AccessProviderTypeGoEdge = AccessProviderType("goedge")
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
AccessProviderTypeKubernetes = AccessProviderType("k8s")
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
AccessProviderTypeLeCDN = AccessProviderType("lecdn")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeMattermost = AccessProviderType("mattermost")
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")
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSSH = AccessProviderType("ssh")
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
AccessProviderTypeTelegramBot = AccessProviderType("telegrambot")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeUpyun = AccessProviderType("upyun")
@@ -63,77 +77,83 @@ const (
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeWangsu = AccessProviderType("wangsu")
AccessProviderTypeWebhook = AccessProviderType("webhook")
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
AccessProviderTypeWestcn = AccessProviderType("westcn")
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
)
type ApplyCAProviderType string
type CAProviderType string
/*
申请证书 CA 提供商常量值。
始终等于授权提供商类型。
注意:如果追加新的常量值,请保持以 ASCII 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
ApplyCAProviderTypeBuypass = ApplyCAProviderType(string(AccessProviderTypeBuypass))
ApplyCAProviderTypeGoogleTrustServices = ApplyCAProviderType(string(AccessProviderTypeGoogleTrustServices))
ApplyCAProviderTypeLetsEncrypt = ApplyCAProviderType(string(AccessProviderTypeLetsEncrypt))
ApplyCAProviderTypeLetsEncryptStaging = ApplyCAProviderType(string(AccessProviderTypeLetsEncryptStaging))
ApplyCAProviderTypeSSLCom = ApplyCAProviderType(string(AccessProviderTypeSSLCOM))
ApplyCAProviderTypeZeroSSL = ApplyCAProviderType(string(AccessProviderTypeZeroSSL))
)
type ApplyDNSProviderType string
/*
申请证书 DNS 提供商常量值。
证书颁发机构提供商常量值。
短横线前的部分始终等于授权提供商类型。
注意:如果追加新的常量值,请保持以 ASCII 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
ApplyDNSProviderTypeACMEHttpReq = ApplyDNSProviderType("acmehttpreq")
ApplyDNSProviderTypeAliyun = ApplyDNSProviderType("aliyun") // 兼容旧值,等同于 [ApplyDNSProviderTypeAliyunDNS]
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns")
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns")
ApplyDNSProviderTypeCloudflare = ApplyDNSProviderType("cloudflare")
ApplyDNSProviderTypeClouDNS = ApplyDNSProviderType("cloudns")
ApplyDNSProviderTypeCMCCCloud = ApplyDNSProviderType("cmcccloud")
ApplyDNSProviderTypeDeSEC = ApplyDNSProviderType("desec")
ApplyDNSProviderTypeDNSLA = ApplyDNSProviderType("dnsla")
ApplyDNSProviderTypeDynv6 = ApplyDNSProviderType("dynv6")
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")
ApplyDNSProviderTypePorkbun = ApplyDNSProviderType("porkbun")
ApplyDNSProviderTypePowerDNS = ApplyDNSProviderType("powerdns")
ApplyDNSProviderTypeRainYun = ApplyDNSProviderType("rainyun")
ApplyDNSProviderTypeTencentCloud = ApplyDNSProviderType("tencentcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeTencentCloudDNS]
ApplyDNSProviderTypeTencentCloudDNS = ApplyDNSProviderType("tencentcloud-dns")
ApplyDNSProviderTypeTencentCloudEO = ApplyDNSProviderType("tencentcloud-eo")
ApplyDNSProviderTypeVercel = ApplyDNSProviderType("vercel")
ApplyDNSProviderTypeVolcEngine = ApplyDNSProviderType("volcengine") // 兼容旧值,等同于 [ApplyDNSProviderTypeVolcEngineDNS]
ApplyDNSProviderTypeVolcEngineDNS = ApplyDNSProviderType("volcengine-dns")
ApplyDNSProviderTypeWestcn = ApplyDNSProviderType("westcn")
CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA)
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
CAProviderTypeLetsEncryptStaging = CAProviderType(AccessProviderTypeLetsEncryptStaging)
CAProviderTypeSSLCom = CAProviderType(AccessProviderTypeSSLCOM)
CAProviderTypeZeroSSL = CAProviderType(AccessProviderTypeZeroSSL)
)
type DeployProviderType string
type ACMEDns01ProviderType string
/*
ACME DNS-01 提供商常量值。
短横线前的部分始终等于授权提供商类型。
注意:如果追加新的常量值,请保持以 ASCII 排序。
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
ACMEDns01ProviderTypeAliyunESA = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-esa")
ACMEDns01ProviderTypeAWS = ACMEDns01ProviderType(AccessProviderTypeAWS) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAWSRoute53]
ACMEDns01ProviderTypeAWSRoute53 = ACMEDns01ProviderType(AccessProviderTypeAWS + "-route53")
ACMEDns01ProviderTypeAzure = ACMEDns01ProviderType(AccessProviderTypeAzure) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAzure]
ACMEDns01ProviderTypeAzureDNS = ACMEDns01ProviderType(AccessProviderTypeAzure + "-dns")
ACMEDns01ProviderTypeBaiduCloud = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeBaiduCloudDNS]
ACMEDns01ProviderTypeBaiduCloudDNS = ACMEDns01ProviderType(AccessProviderTypeBaiduCloud + "-dns")
ACMEDns01ProviderTypeBunny = ACMEDns01ProviderType(AccessProviderTypeBunny)
ACMEDns01ProviderTypeCloudflare = ACMEDns01ProviderType(AccessProviderTypeCloudflare)
ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS)
ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud)
ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC)
ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA)
ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6)
ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore)
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS]
ACMEDns01ProviderTypeJDCloudDNS = ACMEDns01ProviderType(AccessProviderTypeJDCloud + "-dns")
ACMEDns01ProviderTypeNamecheap = ACMEDns01ProviderType(AccessProviderTypeNamecheap)
ACMEDns01ProviderTypeNameDotCom = ACMEDns01ProviderType(AccessProviderTypeNameDotCom)
ACMEDns01ProviderTypeNameSilo = ACMEDns01ProviderType(AccessProviderTypeNameSilo)
ACMEDns01ProviderTypeNetcup = ACMEDns01ProviderType(AccessProviderTypeNetcup)
ACMEDns01ProviderTypeNetlify = ACMEDns01ProviderType(AccessProviderTypeNetlify)
ACMEDns01ProviderTypeNS1 = ACMEDns01ProviderType(AccessProviderTypeNS1)
ACMEDns01ProviderTypePorkbun = ACMEDns01ProviderType(AccessProviderTypePorkbun)
ACMEDns01ProviderTypePowerDNS = ACMEDns01ProviderType(AccessProviderTypePowerDNS)
ACMEDns01ProviderTypeRainYun = ACMEDns01ProviderType(AccessProviderTypeRainYun)
ACMEDns01ProviderTypeTencentCloud = ACMEDns01ProviderType(AccessProviderTypeTencentCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeTencentCloudDNS]
ACMEDns01ProviderTypeTencentCloudDNS = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-dns")
ACMEDns01ProviderTypeTencentCloudEO = ACMEDns01ProviderType(AccessProviderTypeTencentCloud + "-eo")
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
)
type DeploymentProviderType string
/*
部署证书主机提供商常量值。
@@ -143,76 +163,110 @@ type DeployProviderType string
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
DeployProviderType1PanelConsole = DeployProviderType("1panel-console")
DeployProviderType1PanelSite = DeployProviderType("1panel-site")
DeployProviderTypeAliyunALB = DeployProviderType("aliyun-alb")
DeployProviderTypeAliyunCAS = DeployProviderType("aliyun-cas")
DeployProviderTypeAliyunCASDeploy = DeployProviderType("aliyun-casdeploy")
DeployProviderTypeAliyunCDN = DeployProviderType("aliyun-cdn")
DeployProviderTypeAliyunCLB = DeployProviderType("aliyun-clb")
DeployProviderTypeAliyunDCDN = DeployProviderType("aliyun-dcdn")
DeployProviderTypeAliyunESA = DeployProviderType("aliyun-esa")
DeployProviderTypeAliyunFC = DeployProviderType("aliyun-fc")
DeployProviderTypeAliyunLive = DeployProviderType("aliyun-live")
DeployProviderTypeAliyunNLB = DeployProviderType("aliyun-nlb")
DeployProviderTypeAliyunOSS = DeployProviderType("aliyun-oss")
DeployProviderTypeAliyunVOD = DeployProviderType("aliyun-vod")
DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf")
DeployProviderTypeAWSACM = DeployProviderType("aws-acm")
DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront")
DeployProviderTypeAzureKeyVault = DeployProviderType("azure-keyvault")
DeployProviderTypeBaiduCloudAppBLB = DeployProviderType("baiducloud-appblb")
DeployProviderTypeBaiduCloudBLB = DeployProviderType("baiducloud-blb")
DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn")
DeployProviderTypeBaiduCloudCert = DeployProviderType("baiducloud-cert")
DeployProviderTypeBaishanCDN = DeployProviderType("baishan-cdn")
DeployProviderTypeBaotaPanelConsole = DeployProviderType("baotapanel-console")
DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site")
DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn")
DeployProviderTypeCacheFly = DeployProviderType("cachefly")
DeployProviderTypeCdnfly = DeployProviderType("cdnfly")
DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn")
DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications")
DeployProviderTypeGcoreCDN = DeployProviderType("gcore-cdn")
DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn")
DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb")
DeployProviderTypeHuaweiCloudSCM = DeployProviderType("huaweicloud-scm")
DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf")
DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb")
DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn")
DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live")
DeployProviderTypeJDCloudVOD = DeployProviderType("jdcloud-vod")
DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret")
DeployProviderTypeLocal = DeployProviderType("local")
DeployProviderTypeQiniuCDN = DeployProviderType("qiniu-cdn")
DeployProviderTypeQiniuKodo = DeployProviderType("qiniu-kodo")
DeployProviderTypeQiniuPili = DeployProviderType("qiniu-pili")
DeployProviderTypeRainYunRCDN = DeployProviderType("rainyun-rcdn")
DeployProviderTypeSafeLine = DeployProviderType("safeline")
DeployProviderTypeSSH = DeployProviderType("ssh")
DeployProviderTypeTencentCloudCDN = DeployProviderType("tencentcloud-cdn")
DeployProviderTypeTencentCloudCLB = DeployProviderType("tencentcloud-clb")
DeployProviderTypeTencentCloudCOS = DeployProviderType("tencentcloud-cos")
DeployProviderTypeTencentCloudCSS = DeployProviderType("tencentcloud-css")
DeployProviderTypeTencentCloudECDN = DeployProviderType("tencentcloud-ecdn")
DeployProviderTypeTencentCloudEO = DeployProviderType("tencentcloud-eo")
DeployProviderTypeTencentCloudSCF = DeployProviderType("tencentcloud-scf")
DeployProviderTypeTencentCloudSSL = DeployProviderType("tencentcloud-ssl")
DeployProviderTypeTencentCloudSSLDeploy = DeployProviderType("tencentcloud-ssldeploy")
DeployProviderTypeTencentCloudVOD = DeployProviderType("tencentcloud-vod")
DeployProviderTypeTencentCloudWAF = DeployProviderType("tencentcloud-waf")
DeployProviderTypeUCloudUCDN = DeployProviderType("ucloud-ucdn")
DeployProviderTypeUCloudUS3 = DeployProviderType("ucloud-us3")
DeployProviderTypeUpyunCDN = DeployProviderType("upyun-cdn")
DeployProviderTypeUpyunFile = DeployProviderType("upyun-file")
DeployProviderTypeVolcEngineALB = DeployProviderType("volcengine-alb")
DeployProviderTypeVolcEngineCDN = DeployProviderType("volcengine-cdn")
DeployProviderTypeVolcEngineCertCenter = DeployProviderType("volcengine-certcenter")
DeployProviderTypeVolcEngineCLB = DeployProviderType("volcengine-clb")
DeployProviderTypeVolcEngineDCDN = DeployProviderType("volcengine-dcdn")
DeployProviderTypeVolcEngineImageX = DeployProviderType("volcengine-imagex")
DeployProviderTypeVolcEngineLive = DeployProviderType("volcengine-live")
DeployProviderTypeVolcEngineTOS = DeployProviderType("volcengine-tos")
DeployProviderTypeWangsuCDNPro = DeployProviderType("wangsu-cdnpro")
DeployProviderTypeWebhook = DeployProviderType("webhook")
DeploymentProviderType1PanelConsole = DeploymentProviderType(AccessProviderType1Panel + "-console")
DeploymentProviderType1PanelSite = DeploymentProviderType(AccessProviderType1Panel + "-site")
DeploymentProviderTypeAliyunALB = DeploymentProviderType(AccessProviderTypeAliyun + "-alb")
DeploymentProviderTypeAliyunAPIGW = DeploymentProviderType(AccessProviderTypeAliyun + "-apigw")
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")
DeploymentProviderTypeBaotaWAFConsole = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-console")
DeploymentProviderTypeBaotaWAFSite = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-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)
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)
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")
DeploymentProviderTypeRatPanelConsole = DeploymentProviderType(AccessProviderTypeRatPanel + "-console")
DeploymentProviderTypeRatPanelSite = DeploymentProviderType(AccessProviderTypeRatPanel + "-site")
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")
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate")
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)
NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot)
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
)

View File

@@ -13,6 +13,7 @@ type Settings struct {
Content string `json:"content" db:"content"`
}
// Deprecated: v0.4.x 将废弃
type NotifyTemplatesSettingsContent struct {
NotifyTemplates []struct {
Subject string `json:"subject"`
@@ -20,8 +21,10 @@ type NotifyTemplatesSettingsContent struct {
} `json:"notifyTemplates"`
}
// Deprecated: v0.4.x 将废弃
type NotifyChannelsSettingsContent map[string]map[string]any
// Deprecated: v0.4.x 将废弃
func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error) {
conf := &NotifyChannelsSettingsContent{}
if err := json.Unmarshal([]byte(s.Content), conf); err != nil {

View File

@@ -3,7 +3,7 @@ package domain
import (
"time"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
)
const CollectionNameWorkflow = "workflow"
@@ -71,10 +71,11 @@ type WorkflowNodeConfigForApply struct {
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值将使用全局配置)
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法
KeyAlgorithm string `json:"keyAlgorithm"` // 证书算法
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播超时时间(零值取决于提供商的默认值)
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS TTL(零值取决于提供商的默认值)
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
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
@@ -95,33 +96,32 @@ type WorkflowNodeConfigForDeploy struct {
}
type WorkflowNodeConfigForNotify struct {
Channel string `json:"channel"` // 通知渠道
Subject string `json:"subject"` // 通知主题
Message string `json:"message"` // 通知内容
Channel string `json:"channel,omitempty"` // Deprecated: v0.4.x 将废弃
Provider string `json:"provider"` // 通知提供商
ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
Subject string `json:"subject"` // 通知主题
Message string `json:"message"` // 通知内容
}
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
if skipBeforeExpiryDays == 0 {
skipBeforeExpiryDays = 30
}
return WorkflowNodeConfigForApply{
Domains: maputil.GetString(n.Config, "domains"),
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
Provider: maputil.GetString(n.Config, "provider"),
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
ProviderConfig: maputil.GetAnyMap(n.Config, "providerConfig"),
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
CAProvider: maputil.GetString(n.Config, "caProvider"),
CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"),
CAProviderConfig: maputil.GetAnyMap(n.Config, "caProviderConfig"),
CAProviderConfig: maputil.GetKVMapAny(n.Config, "caProviderConfig"),
KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"),
Nameservers: maputil.GetString(n.Config, "nameservers"),
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: maputil.GetOrDefaultInt32(n.Config, "skipBeforeExpiryDays", 30),
}
}
@@ -138,16 +138,19 @@ func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
Certificate: maputil.GetString(n.Config, "certificate"),
Provider: maputil.GetString(n.Config, "provider"),
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
ProviderConfig: maputil.GetAnyMap(n.Config, "providerConfig"),
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
}
}
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
return WorkflowNodeConfigForNotify{
Channel: maputil.GetString(n.Config, "channel"),
Subject: maputil.GetString(n.Config, "subject"),
Message: maputil.GetString(n.Config, "message"),
Channel: maputil.GetString(n.Config, "channel"),
Provider: maputil.GetString(n.Config, "provider"),
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
ProviderConfig: maputil.GetKVMapAny(n.Config, "providerConfig"),
Subject: maputil.GetString(n.Config, "subject"),
Message: maputil.GetString(n.Config, "message"),
}
}

View 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 := &notifierProviderOptions{
Provider: domain.NotificationProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderServiceConfig: 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 &notifierImpl{
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
}

View File

@@ -9,10 +9,11 @@ import (
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
"github.com/usual2970/certimate/internal/repository"
)
// Deprecated: v0.4.x 将废弃
func SendToAllChannels(subject, message string) error {
notifiers, err := getEnabledNotifiers()
if err != nil {
@@ -38,8 +39,9 @@ func SendToAllChannels(subject, message string) error {
return err
}
// Deprecated: v0.4.x 将废弃
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 {
return err
}
@@ -48,6 +50,7 @@ func SendToChannel(subject, message string, channel string, channelConfig map[st
return err
}
// Deprecated: v0.4.x 将废弃
func getEnabledNotifiers() ([]notifier.Notifier, error) {
settingsRepo := repository.NewSettingsRepository()
settings, err := settingsRepo.GetByName(context.Background(), "notifyChannels")
@@ -66,7 +69,7 @@ func getEnabledNotifiers() ([]notifier.Notifier, error) {
continue
}
notifier, err := createNotifier(domain.NotifyChannelType(k), v)
notifier, err := createNotifierProviderUseGlobalSettings(domain.NotifyChannelType(k), v)
if err != nil {
continue
}

View File

@@ -2,105 +2,152 @@ package notify
import (
"fmt"
"net/http"
"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"
pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
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"
pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
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"
pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
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
ProviderServiceConfig map[string]any
}
func createNotifierProvider(options *notifierProviderOptions) (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"),
})
switch options.Provider {
case domain.NotificationProviderTypeDingTalkBot:
{
access := domain.AccessConfigForDingTalkBot{}
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{
AccessToken: maputil.GetString(channelConfig, "accessToken"),
Secret: maputil.GetString(channelConfig, "secret"),
})
return pDingTalkBot.NewNotifier(&pDingTalkBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
Secret: access.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.NotificationProviderTypeEmail:
{
access := domain.AccessConfigForEmail{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
case domain.NotifyChannelTypeGotify:
return pGotify.NewNotifier(&pGotify.NotifierConfig{
Url: maputil.GetString(channelConfig, "url"),
Token: maputil.GetString(channelConfig, "token"),
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
})
return pEmail.NewNotifier(&pEmail.NotifierConfig{
SmtpHost: access.SmtpHost,
SmtpPort: access.SmtpPort,
SmtpTls: access.SmtpTls,
Username: access.Username,
Password: access.Password,
SenderAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "senderAddress", access.DefaultSenderAddress),
ReceiverAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.DefaultReceiverAddress),
})
}
case domain.NotifyChannelTypeLark:
return pLark.NewNotifier(&pLark.NotifierConfig{
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
})
case domain.NotificationProviderTypeLarkBot:
{
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.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"),
})
return pLarkBot.NewNotifier(&pLarkBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
})
}
case domain.NotifyChannelTypePushPlus:
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
Token: maputil.GetString(channelConfig, "token"),
})
case domain.NotificationProviderTypeMattermost:
{
access := domain.AccessConfigForMattermost{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
case domain.NotifyChannelTypeServerChan:
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
Url: maputil.GetString(channelConfig, "url"),
})
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
ServerUrl: access.ServerUrl,
Username: access.Username,
Password: access.Password,
ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId),
})
}
case domain.NotifyChannelTypeTelegram:
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
ApiToken: maputil.GetString(channelConfig, "apiToken"),
ChatId: maputil.GetInt64(channelConfig, "chatId"),
})
case domain.NotificationProviderTypeTelegramBot:
{
access := domain.AccessConfigForTelegramBot{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
case domain.NotifyChannelTypeWebhook:
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
Url: maputil.GetString(channelConfig, "url"),
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"),
})
return pTelegramBot.NewNotifier(&pTelegramBot.NotifierConfig{
BotToken: access.BotToken,
ChatId: maputil.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.DefaultChatId),
})
}
case domain.NotifyChannelTypeWeCom:
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
})
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.ProviderServiceConfig, "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.ProviderServiceConfig, "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 pWeComBot.NewNotifier(&pWeComBot.NotifierConfig{
WebhookUrl: access.WebhookUrl,
})
}
}
return nil, fmt.Errorf("unsupported notifier channel: %s", channelConfig)
return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider)
}

View 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/dingtalkbot"
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/larkbot"
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/telegrambot"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
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{
ServerUrl: 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{
ServerUrl: 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)
}

View File

@@ -8,25 +8,30 @@ import (
"github.com/usual2970/certimate/internal/domain/dtos"
)
// Deprecated: v0.4.x 将废弃
const (
notifyTestTitle = "测试通知"
notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。"
)
// Deprecated: v0.4.x 将废弃
type settingsRepository interface {
GetByName(ctx context.Context, name string) (*domain.Settings, error)
}
// Deprecated: v0.4.x 将废弃
type NotifyService struct {
settingsRepo settingsRepository
}
// Deprecated: v0.4.x 将废弃
func NewNotifyService(settingsRepo settingsRepository) *NotifyService {
return &NotifyService{
settingsRepo: settingsRepo,
}
}
// Deprecated: v0.4.x 将废弃
func (n *NotifyService) Test(ctx context.Context, req *dtos.NotifyTestPushReq) error {
settings, err := n.settingsRepo.GetByName(ctx, "notifyChannels")
if err != nil {

View File

@@ -1,4 +1,4 @@
package acmehttpreq
package acmehttpreq
import (
"net/url"

View File

@@ -0,0 +1,41 @@
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
providerConfig.RegionID = config.Region
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
}

View File

@@ -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
}
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/go-acme/lego/v4/challenge"
"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 {

View File

@@ -1,4 +1,4 @@
package lego_baiducloud
package lego_baiducloud
import (
"errors"
@@ -89,7 +89,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -109,7 +109,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -128,7 +128,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
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 := ""
pageSize := 1000
for {
@@ -159,7 +159,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*bcedns.Record,
}
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}
@@ -186,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
}
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}

View File

@@ -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
}

View File

@@ -93,7 +93,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -108,33 +108,33 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
}
if record == nil {
// add new record
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
LineId: "0", // 默认线路
Rr: subDomain,
DomainName: readDomain,
Description: "from certimate",
Description: "certimate acme",
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
Value: info.Value,
Ttl: &d.config.TTL,
},
})
if err != nil {
return fmt.Errorf("lego: %w", err)
return fmt.Errorf("cmccecloud: %w", err)
}
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
} else {
// update record
resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{
ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{
RecordId: record.RecordId,
Rr: subDomain,
DomainName: readDomain,
Description: "from certmate",
Description: "certmate acme",
LineId: "0",
Type: model.ModifyRecordOpenapiBodyTypeEnumTxt,
Value: info.Value,
@@ -142,44 +142,52 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
},
})
if err != nil {
return fmt.Errorf("lego: %w", err)
return fmt.Errorf("cmccecloud: %w", err)
}
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
}
}
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN)
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)
if err != nil {
return fmt.Errorf("cmccecloud: %w", err)
}
readDomain := strings.Trim(zoneName, ".")
record, err := d.getDomainRecord(readDomain, subDomain)
if err != nil {
return err
}
if record == 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
}
@@ -205,8 +213,9 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco
}
if resp.State != model.ListRecordOpenapiResponseStateEnumOk {
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 {
for _, item := range *resp.Body.Data {
if item.Rr == rr {
@@ -214,9 +223,11 @@ func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListReco
}
}
}
if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages {
return nil, nil
}
page++
}
}

View File

@@ -1,4 +1,4 @@
package lego_dnsla
package lego_dnsla
import (
"errors"
@@ -10,7 +10,7 @@ import (
"github.com/go-acme/lego/v4/challenge/dns01"
"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 (
@@ -83,7 +83,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -103,7 +103,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)

View File

@@ -1,4 +1,4 @@
package lego_dynv6
package lego_dynv6
import (
"context"
@@ -76,7 +76,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
@@ -96,7 +96,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
@@ -115,7 +115,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*libdns.Record, error) {
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*libdns.Record, error) {
records, err := d.client.GetRecords(context.Background(), zoneName)
if err != nil {
return nil, err
@@ -131,7 +131,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*libdns.Record,
}
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}
@@ -153,7 +153,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
}
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}

View File

@@ -1,4 +1,4 @@
package lego_gname
package lego_gname
import (
"errors"
@@ -9,7 +9,7 @@ import (
"github.com/go-acme/lego/v4/challenge/dns01"
"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 (
@@ -82,7 +82,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -102,7 +102,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -121,7 +121,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
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 := int32(1)
pageSize := int32(20)
for {
@@ -155,7 +155,7 @@ func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.Resolu
}
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}
@@ -186,7 +186,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
}
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
record, err := d.findDNSRecord(zoneName, subDomain)
if err != nil {
return err
}

View File

@@ -1,4 +1,4 @@
package lego_jdcloud
package lego_jdcloud
import (
"errors"
@@ -91,7 +91,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)
@@ -111,7 +111,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -1,6 +1,8 @@
package powerdns
import (
"crypto/tls"
"net/http"
"net/url"
"time"
@@ -9,10 +11,11 @@ import (
)
type ChallengeProviderConfig struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
@@ -24,6 +27,13 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
providerConfig := pdns.NewDefaultConfig()
providerConfig.Host = host
providerConfig.APIKey = config.ApiKey
if config.AllowInsecureConnections {
providerConfig.HTTPClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
}
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}

View File

@@ -1,4 +1,4 @@
package lego_tencentcloudeo
package lego_tencentcloudeo
import (
"errors"
@@ -20,7 +20,7 @@ const (
EnvSecretID = envNamespace + "SECRET_ID"
EnvSecretKey = envNamespace + "SECRET_KEY"
EnvZoneId = envNamespace + "ZONE_ID"
EnvZoneID = envNamespace + "ZONE_ID"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@@ -33,7 +33,7 @@ var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
SecretID string
SecretKey string
ZoneId string
ZoneID string
PropagationTimeout time.Duration
PollingInterval time.Duration
@@ -56,7 +56,7 @@ func NewDefaultConfig() *Config {
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneId)
values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneID)
if err != nil {
return nil, fmt.Errorf("tencentcloud-eo: %w", err)
}
@@ -64,7 +64,7 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig()
config.SecretID = values[EnvSecretID]
config.SecretKey = values[EnvSecretKey]
config.ZoneId = values[EnvSecretKey]
config.ZoneID = values[EnvSecretKey]
return NewDNSProviderConfig(config)
}
@@ -112,12 +112,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) {
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.ZoneId = common.StringPtr(d.config.ZoneID)
request.Offset = common.Int64Ptr(int64(pageOffset))
request.Limit = common.Int64Ptr(int64(pageLimit))
request.Filters = []*teo.AdvancedFilter{
@@ -141,7 +141,7 @@ func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error)
}
}
if len(response.Response.DnsRecords) < int(pageLimit) {
if len(response.Response.DnsRecords) < pageLimit {
break
}
@@ -153,14 +153,14 @@ func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error)
}
func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
record, err := d.getDNSRecord(effectiveFQDN)
record, err := d.findDNSRecord(effectiveFQDN)
if err != nil {
return err
}
if record == nil {
request := teo.NewCreateDnsRecordRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.Name = common.StringPtr(effectiveFQDN)
request.Type = common.StringPtr("TXT")
request.Content = common.StringPtr(value)
@@ -169,8 +169,9 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
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.ZoneId = common.StringPtr(d.config.ZoneID)
request.DnsRecords = []*teo.DnsRecord{record}
if _, err := d.client.ModifyDnsRecords(request); err != nil {
return err
@@ -178,7 +179,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
if *record.Status == "disable" {
request := teo.NewModifyDnsRecordsStatusRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.RecordsToEnable = []*string{record.RecordId}
if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil {
return err
@@ -190,7 +191,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
}
func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error {
record, err := d.getDNSRecord(effectiveFQDN)
record, err := d.findDNSRecord(effectiveFQDN)
if err != nil {
return err
}
@@ -199,7 +200,7 @@ func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error {
return nil
} else {
request := teo.NewDeleteDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.ZoneId = common.StringPtr(d.config.ZoneID)
request.RecordIds = []*string{record.RecordId}
_, err = d.client.DeleteDnsRecords(request)
return err

View File

@@ -24,7 +24,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
providerConfig := internal.NewDefaultConfig()
providerConfig.SecretID = config.SecretId
providerConfig.SecretKey = config.SecretKey
providerConfig.ZoneId = config.ZoneId
providerConfig.ZoneID = config.ZoneId
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}

View File

@@ -1,4 +1,4 @@
package deployer
package deployer
import (
"context"
@@ -14,13 +14,13 @@ type Deployer interface {
//
// 入参:
// - ctx上下文。
// - certPem:证书 PEM 内容。
// - privkeyPem:私钥 PEM 内容。
// - certPEM:证书 PEM 内容。
// - privkeyPEM:私钥 PEM 内容。
//
// 出参:
// - res部署结果。
// - 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)
}
// 表示证书部署结果的数据结构。

View File

@@ -1,21 +1,23 @@
package onepanelconsole
package onepanelconsole
import (
"context"
"crypto/tls"
"errors"
"fmt"
"log/slog"
"net/url"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
// 1Panel 版本。
// 可取值 "v1"、"v2"。
ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
@@ -27,7 +29,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *opsdk.Client
sdkClient *onepanelsdk.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -37,9 +39,9 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
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{
@@ -58,11 +60,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 证书
updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
Cert: certPem,
Key: privkeyPem,
updateSystemSSLReq := &onepanelsdk.UpdateSystemSSLRequest{
Cert: certPEM,
Key: privkeyPEM,
SSL: "enable",
SSLType: "import-paste",
}
@@ -74,23 +76,27 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
updateSystemSSLResp, err := d.sdkClient.UpdateSystemSSL(updateSystemSSLReq)
d.logger.Debug("sdk request '1panel.UpdateSystemSSL'", slog.Any("request", updateSystemSSLReq), slog.Any("response", updateSystemSSLResp))
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)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) {
func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
if apiVersion == "" {
return nil, errors.New("invalid 1panel api version")
}
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
client := opsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
if skipTlsVerify {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}

View File

@@ -1,4 +1,4 @@
package onepanelconsole_test
package onepanelconsole_test
import (
"context"
@@ -15,6 +15,7 @@ var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
fApiVersion string
fApiKey string
)
@@ -24,6 +25,7 @@ func init() {
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
}
@@ -34,6 +36,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIVERSION="v1" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key"
*/
func TestDeploy(t *testing.T) {
@@ -45,11 +48,13 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("APIURL: %v", fApiUrl),
fmt.Sprintf("APIVERSION: %v", fApiVersion),
fmt.Sprintf("APIKEY: %v", fApiKey),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiVersion: fApiVersion,
ApiKey: fApiKey,
AllowInsecureConnections: true,
AutoRestart: true,

View File

@@ -1,36 +1,44 @@
package onepanelsite
package onepanelsite
import (
"context"
"crypto/tls"
"errors"
"fmt"
"log/slog"
"net/url"
"strconv"
xerrors "github.com/pkg/errors"
"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/1panel-ssl"
opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
// 1Panel 版本。
// 可取值 "v1"、"v2"。
ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
// 部署资源类型。
ResourceType ResourceType `json:"resourceType"`
// 网站 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 {
config *DeployerConfig
logger *slog.Logger
sdkClient *opsdk.Client
sdkClient *onepanelsdk.Client
sslUploader uploader.Uploader
}
@@ -41,17 +49,18 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
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{
ApiUrl: config.ApiUrl,
ApiKey: config.ApiKey,
ApiUrl: config.ApiUrl,
ApiVersion: config.ApiVersion,
ApiKey: config.ApiKey,
})
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{
@@ -72,28 +81,52 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 配置
getHttpsConfReq := &opsdk.GetHttpsConfRequest{
getHttpsConfReq := &onepanelsdk.GetHttpsConfRequest{
WebsiteID: d.config.WebsiteId,
}
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
d.logger.Debug("sdk request '1panel.GetHttpsConf'", slog.Any("request", getHttpsConfReq), slog.Any("response", getHttpsConfResp))
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)
}
// 上传证书到面板
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
return fmt.Errorf("failed to upload certificate file: %w", err)
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 修改网站 HTTPS 配置
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{
WebsiteID: d.config.WebsiteId,
Type: "existed",
WebsiteSSLID: certId,
@@ -106,23 +139,59 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
updateHttpsConfResp, err := d.sdkClient.UpdateHttpsConf(updateHttpsConfReq)
d.logger.Debug("sdk request '1panel.UpdateHttpsConf'", slog.Any("request", updateHttpsConfReq), slog.Any("response", updateHttpsConfResp))
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)
}
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 := &onepanelsdk.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 := &onepanelsdk.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, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
if apiVersion == "" {
return nil, errors.New("invalid 1panel api version")
}
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
client := opsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
if skipTlsVerify {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}

View File

@@ -1,4 +1,4 @@
package onepanelsite_test
package onepanelsite_test
import (
"context"
@@ -15,16 +15,18 @@ var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
fApiVersion string
fApiKey string
fWebsiteId int64
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
argsPrefix := "CERTIMATE_DEPLOYER_1PANELSITE_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
}
@@ -32,12 +34,13 @@ func init() {
/*
Shell command to run this test:
go test -v ./1panel_console_test.go -args \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" \
--CERTIMATE_DEPLOYER_1PANELCONSOLE_WEBSITEID="your-website-id"
go test -v ./1panel_site_test.go -args \
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \
--CERTIMATE_DEPLOYER_1PANELSITE_APIVERSION="v1" \
--CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \
--CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
*/
func TestDeploy(t *testing.T) {
flag.Parse()
@@ -48,15 +51,18 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("APIURL: %v", fApiUrl),
fmt.Sprintf("APIVERSION: %v", fApiVersion),
fmt.Sprintf("APIKEY: %v", fApiKey),
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiVersion: fApiVersion,
ApiKey: fApiKey,
WebsiteId: fWebsiteId,
AllowInsecureConnections: true,
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
WebsiteId: fWebsiteId,
})
if err != nil {
t.Errorf("err: %+v", err)

View File

@@ -0,0 +1,10 @@
package onepanelsite
type ResourceType string
const (
// 资源类型:替换指定网站的证书。
RESOURCE_TYPE_WEBSITE = ResourceType("website")
// 资源类型:替换指定证书。
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
)

View File

@@ -1,4 +1,4 @@
package aliyunalb
package aliyunalb
import (
"context"
@@ -13,7 +13,6 @@ import (
alicas "github.com/alibabacloud-go/cas-20200407/v3/client"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
xerrors "github.com/pkg/errors"
"golang.org/x/exp/slices"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
@@ -62,12 +61,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
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)
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{
@@ -88,11 +87,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -110,7 +109,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
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
@@ -129,7 +128,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'")
return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err)
}
// 查询 HTTPS 监听列表
@@ -138,6 +137,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
listListenersLimit := int32(100)
var listListenersToken *string = nil
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenersReq := &alialb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
@@ -147,12 +152,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
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 {
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 {
for _, listener := range listListenersResp.Body.Listeners {
listenerIds = append(listenerIds, *listener.ListenerId)
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -167,6 +172,12 @@ 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-listlisteners
listListenersToken = nil
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenersReq := &alialb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
@@ -176,12 +187,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
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 {
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 {
for _, listener := range listListenersResp.Body.Listeners {
listenerIds = append(listenerIds, *listener.ListenerId)
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -200,8 +211,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
for _, listenerId := range listenerIds {
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -235,7 +251,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'")
return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err)
}
if d.config.Domain == "" {
@@ -252,7 +268,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'")
return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err)
}
} else {
// 指定 SNI需部署到扩展域名
@@ -263,6 +279,12 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
listListenerCertificatesLimit := int32(100)
var listListenerCertificatesToken *string = nil
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
NextToken: listListenerCertificatesToken,
MaxResults: tea.Int32(listListenerCertificatesLimit),
@@ -272,7 +294,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
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 {
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 {
@@ -331,7 +353,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
}
}
errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'cas.GetUserCertificateDetail'"))
errs = append(errs, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err))
continue
} else {
certCNMatched := tea.StringValue(getUserCertificateDetailResp.Body.Common) == d.config.Domain
@@ -368,7 +390,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener'")
return fmt.Errorf("failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener': %w", err)
}
}
@@ -389,7 +411,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener'")
return fmt.Errorf("failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener': %w", err)
}
}
}
@@ -447,7 +469,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
// 阿里云 CAS 服务接入点是独立于 ALB 服务的
// 国内版固定接入点:华东一杭州
// 国际版固定接入点:亚太东南一新加坡
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
if !strings.HasPrefix(casRegion, "cn-") {
casRegion = "ap-southeast-1"
} else {
casRegion = "cn-hangzhou"

View File

@@ -1,4 +1,4 @@
package aliyunalb_test
package aliyunalb_test
import (
"context"
@@ -96,6 +96,7 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("LISTENERID: %v", fListenerId),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{

View File

@@ -1,4 +1,4 @@
package aliyunalb
package aliyunalb
type ResourceType string

View File

@@ -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
}

View File

@@ -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)
})
}

View File

@@ -0,0 +1,10 @@
package aliyunapigw
type ServiceType string
const (
// 服务类型:原 API 网关。
SERVICE_TYPE_TRADITIONAL = ServiceType("traditional")
// 服务类型:云原生 API 网关。
SERVICE_TYPE_CLOUDNATIVE = ServiceType("cloudnative")
)

View File

@@ -1,4 +1,4 @@
package aliyuncasdeploy
package aliyuncasdeploy
import (
"context"
@@ -11,7 +11,6 @@ import (
alicas "github.com/alibabacloud-go/cas-20200407/v3/client"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"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/uploader"
@@ -48,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -57,7 +56,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
Region: config.Region,
})
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{
@@ -78,15 +77,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 {
return nil, errors.New("config `resourceIds` is required")
}
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -101,7 +100,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
listContactResp, err := d.sdkClient.ListContact(listContactReq)
d.logger.Debug("sdk request 'cas.ListContact'", slog.Any("request", listContactReq), slog.Any("response", listContactResp))
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 {
@@ -121,14 +120,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
createDeploymentJobResp, err := d.sdkClient.CreateDeploymentJob(createDeploymentJobReq)
d.logger.Debug("sdk request 'cas.CreateDeploymentJob'", slog.Any("request", createDeploymentJobReq), slog.Any("response", createDeploymentJobResp))
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)
}
// 循环获取部署任务详情,等待任务状态变更
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
for {
if ctx.Err() != nil {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
@@ -137,7 +138,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
describeDeploymentJobResp, err := d.sdkClient.DescribeDeploymentJob(describeDeploymentJobReq)
d.logger.Debug("sdk request 'cas.DescribeDeploymentJob'", slog.Any("request", describeDeploymentJobReq), slog.Any("response", describeDeploymentJobResp))
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" {

View File

@@ -1,11 +1,10 @@
package aliyuncas
package aliyuncas
import (
"context"
"fmt"
"log/slog"
xerrors "github.com/pkg/errors"
"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"
@@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
Region: config.Region,
})
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{
@@ -59,11 +58,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}

View File

@@ -1,4 +1,4 @@
package aliyuncdn
package aliyuncdn
import (
"context"
@@ -10,7 +10,6 @@ import (
alicdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
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{
@@ -58,7 +57,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 要求的泛域名格式
domain := strings.TrimPrefix(d.config.Domain, "*")
@@ -69,13 +68,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
SSLProtocol: tea.String("on"),
SSLPub: tea.String(certPem),
SSLPri: tea.String(privkeyPem),
SSLPub: tea.String(certPEM),
SSLPri: tea.String(privkeyPEM),
}
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
d.logger.Debug("sdk request 'cdn.SetCdnDomainSSLCertificate'", slog.Any("request", setCdnDomainSSLCertificateReq), slog.Any("response", setCdnDomainSSLCertificateResp))
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)
}
return &deployer.DeployResult{}, nil

View File

@@ -1,4 +1,4 @@
package aliyuncdn_test
package aliyuncdn_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunclb
package aliyunclb
import (
"context"
@@ -9,7 +9,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
alislb "github.com/alibabacloud-go/slb-20140515/v4/client"
"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/uploader"
@@ -52,16 +51,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
AccessKeyId: config.AccessKeyId,
AccessKeySecret: config.AccessKeySecret,
Region: config.Region,
})
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -82,11 +77,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -104,7 +99,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
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
@@ -124,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeLoadBalancerAttributeResp, err := d.sdkClient.DescribeLoadBalancerAttribute(describeLoadBalancerAttributeReq)
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerAttribute'", slog.Any("request", describeLoadBalancerAttributeReq), slog.Any("response", describeLoadBalancerAttributeResp))
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)
}
// 查询 HTTPS 监听列表
@@ -133,6 +128,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeLoadBalancerListenersLimit := int32(100)
var describeLoadBalancerListenersToken *string = nil
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
RegionId: tea.String(d.config.Region),
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
@@ -143,7 +144,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeLoadBalancerListenersResp, err := d.sdkClient.DescribeLoadBalancerListeners(describeLoadBalancerListenersReq)
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerListeners'", slog.Any("request", describeLoadBalancerListenersReq), slog.Any("response", describeLoadBalancerListenersResp))
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 {
@@ -167,8 +168,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
var errs []error
for _, listenerPort := range listenerPorts {
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -206,7 +212,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
describeLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.DescribeLoadBalancerHTTPSListenerAttribute(describeLoadBalancerHTTPSListenerAttributeReq)
d.logger.Debug("sdk request 'slb.DescribeLoadBalancerHTTPSListenerAttribute'", slog.Any("request", describeLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", describeLoadBalancerHTTPSListenerAttributeResp))
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)
}
if d.config.Domain == "" {
@@ -223,7 +229,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.SetLoadBalancerHTTPSListenerAttribute(setLoadBalancerHTTPSListenerAttributeReq)
d.logger.Debug("sdk request 'slb.SetLoadBalancerHTTPSListenerAttribute'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp))
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)
}
} else {
// 指定 SNI需部署到扩展域名
@@ -238,7 +244,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
describeDomainExtensionsResp, err := d.sdkClient.DescribeDomainExtensions(describeDomainExtensionsReq)
d.logger.Debug("sdk request 'slb.DescribeDomainExtensions'", slog.Any("request", describeDomainExtensionsReq), slog.Any("response", describeDomainExtensionsResp))
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)
}
// 遍历修改扩展域名
@@ -259,7 +265,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
setDomainExtensionAttributeResp, err := d.sdkClient.SetDomainExtensionAttribute(setDomainExtensionAttributeReq)
d.logger.Debug("sdk request 'slb.SetDomainExtensionAttribute'", slog.Any("request", setDomainExtensionAttributeReq), slog.Any("response", setDomainExtensionAttributeResp))
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
}
}
@@ -300,3 +306,12 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*alislb.Clien
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
}

View File

@@ -1,4 +1,4 @@
package aliyunclb_test
package aliyunclb_test
import (
"context"
@@ -18,7 +18,7 @@ var (
fAccessKeySecret string
fRegion string
fLoadbalancerId string
fListenerPort int
fListenerPort int64
fDomain string
)
@@ -31,7 +31,7 @@ func init() {
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
flag.IntVar(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
flag.Int64Var(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}

View File

@@ -1,4 +1,4 @@
package aliyunclb
package aliyunclb
type ResourceType string

View File

@@ -1,4 +1,4 @@
package aliyundcdn
package aliyundcdn
import (
"context"
@@ -10,7 +10,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
alidcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
"github.com/alibabacloud-go/tea/tea"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -39,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
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{
@@ -58,7 +57,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 要求的泛域名格式
domain := strings.TrimPrefix(d.config.Domain, "*")
@@ -69,13 +68,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
SSLProtocol: tea.String("on"),
SSLPub: tea.String(certPem),
SSLPri: tea.String(privkeyPem),
SSLPub: tea.String(certPEM),
SSLPri: tea.String(privkeyPEM),
}
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
d.logger.Debug("sdk request 'dcdn.SetDcdnDomainSSLCertificate'", slog.Any("request", setDcdnDomainSSLCertificateReq), slog.Any("response", setDcdnDomainSSLCertificateResp))
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)
}
return &deployer.DeployResult{}, nil

View File

@@ -1,4 +1,4 @@
package aliyundcdn_test
package aliyundcdn_test
import (
"context"

View 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
}

View File

@@ -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)
})
}

View File

@@ -1,4 +1,4 @@
package aliyunesa
package aliyunesa
import (
"context"
@@ -11,7 +11,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliesa "github.com/alibabacloud-go/esa-20240910/v2/client"
"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/uploader"
@@ -45,12 +44,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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)
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{
@@ -71,15 +70,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 {
return nil, errors.New("config `siteId` is required")
}
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -95,7 +94,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
setCertificateResp, err := d.sdkClient.SetCertificate(setCertificateReq)
d.logger.Debug("sdk request 'esa.SetCertificate'", slog.Any("request", setCertificateReq), slog.Any("response", setCertificateResp))
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)
}
return &deployer.DeployResult{}, nil
@@ -123,7 +122,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
// 阿里云 CAS 服务接入点是独立于 ESA 服务的
// 国内版固定接入点:华东一杭州
// 国际版固定接入点:亚太东南一新加坡
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
if !strings.HasPrefix(casRegion, "cn-") {
casRegion = "ap-southeast-1"
} else {
casRegion = "cn-hangzhou"

View File

@@ -1,4 +1,4 @@
package aliyunesa_test
package aliyunesa_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunfc
package aliyunfc
import (
"context"
@@ -10,7 +10,6 @@ import (
alifc3 "github.com/alibabacloud-go/fc-20230330/v4/client"
alifc2 "github.com/alibabacloud-go/fc-open-20210406/v2/client"
"github.com/alibabacloud-go/tea/tea"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -23,6 +22,7 @@ type DeployerConfig struct {
// 阿里云地域。
Region string `json:"region"`
// 服务版本。
// 可取值 "2.0"、"3.0"。
ServiceVersion string `json:"serviceVersion"`
// 自定义域名(支持泛域名)。
Domain string `json:"domain"`
@@ -48,7 +48,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
clients, err := createSdkClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -67,32 +67,32 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 {
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
}
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
}
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
}
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
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'")
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
}
// 更新自定义域名
@@ -101,8 +101,8 @@ func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, priv
Body: &alifc3.UpdateCustomDomainInput{
CertConfig: &alifc3.CertConfig{
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
Certificate: tea.String(certPem),
PrivateKey: tea.String(privkeyPem),
Certificate: tea.String(certPEM),
PrivateKey: tea.String(privkeyPEM),
},
Protocol: getCustomDomainResp.Body.Protocol,
TlsConfig: getCustomDomainResp.Body.TlsConfig,
@@ -111,19 +111,19 @@ func (d *DeployerProvider) deployToFC3(ctx context.Context, certPem string, priv
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'")
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
}
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
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'fc.GetCustomDomain'")
return fmt.Errorf("failed to execute sdk request 'fc.GetCustomDomain': %w", err)
}
// 更新自定义域名
@@ -131,8 +131,8 @@ func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, priv
updateCustomDomainReq := &alifc2.UpdateCustomDomainRequest{
CertConfig: &alifc2.CertConfig{
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
Certificate: tea.String(certPem),
PrivateKey: tea.String(privkeyPem),
Certificate: tea.String(certPEM),
PrivateKey: tea.String(privkeyPEM),
},
Protocol: getCustomDomainResp.Body.Protocol,
TlsConfig: getCustomDomainResp.Body.TlsConfig,
@@ -140,7 +140,7 @@ func (d *DeployerProvider) deployToFC2(ctx context.Context, certPem string, priv
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 {
return xerrors.Wrap(err, "failed to execute sdk request 'fc.UpdateCustomDomain'")
return fmt.Errorf("failed to execute sdk request 'fc.UpdateCustomDomain': %w", err)
}
return nil

View File

@@ -1,4 +1,4 @@
package aliyunfc_test
package aliyunfc_test
import (
"context"

View File

@@ -0,0 +1,322 @@
package aliyunga
import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliga "github.com/alibabacloud-go/ga-20191120/v3/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"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
)
type DeployerConfig struct {
// 阿里云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 阿里云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 部署资源类型。
ResourceType ResourceType `json:"resourceType"`
// 全球加速实例 ID。
AcceleratorId string `json:"acceleratorId"`
// 全球加速监听 ID。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
ListenerId string `json:"listenerId,omitempty"`
// SNI 域名(不支持泛域名)。
// 部署资源类型为 [RESOURCE_TYPE_ACCELERATOR]、[RESOURCE_TYPE_LISTENER] 时选填。
Domain string `json:"domain,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *aliga.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)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret)
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) {
// 上传证书到 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))
}
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_ACCELERATOR:
if err := d.deployToAccelerator(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
return nil, err
}
case RESOURCE_TYPE_LISTENER:
if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
}
return &deployer.DeployResult{}, nil
}
func (d *DeployerProvider) deployToAccelerator(ctx context.Context, cloudCertId string) error {
if d.config.AcceleratorId == "" {
return errors.New("config `acceleratorId` is required")
}
// 查询 HTTPS 监听列表
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlisteners
listenerIds := make([]string, 0)
listListenersPageNumber := int32(1)
listListenersPageSize := int32(50)
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenersReq := &aliga.ListListenersRequest{
RegionId: tea.String("cn-hangzhou"),
AcceleratorId: tea.String(d.config.AcceleratorId),
PageNumber: tea.Int32(listListenersPageNumber),
PageSize: tea.Int32(listListenersPageSize),
}
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'ga.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ga.ListListeners': %w", err)
}
if listListenersResp.Body.Listeners != nil {
for _, listener := range listListenersResp.Body.Listeners {
if strings.EqualFold(tea.StringValue(listener.Protocol), "https") {
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
}
if len(listListenersResp.Body.Listeners) < int(listListenersPageSize) {
break
} else {
listListenersPageNumber++
}
}
// 遍历更新监听证书
if len(listenerIds) == 0 {
d.logger.Info("no ga listeners to deploy")
} else {
var errs []error
d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
for _, listenerId := range listenerIds {
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
if d.config.AcceleratorId == "" {
return errors.New("config `acceleratorId` is required")
}
if d.config.ListenerId == "" {
return errors.New("config `listenerId` is required")
}
// 更新监听
if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, d.config.ListenerId, cloudCertId); err != nil {
return err
}
return nil
}
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudAcceleratorId string, cloudListenerId string, cloudCertId string) error {
// 查询监听绑定的证书列表
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlistenercertificates
var listenerDefaultCertificate *aliga.ListListenerCertificatesResponseBodyCertificates
var listenerAdditionalCertificates []*aliga.ListListenerCertificatesResponseBodyCertificates = make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0)
var listListenerCertificatesNextToken *string
for {
listListenerCertificatesReq := &aliga.ListListenerCertificatesRequest{
RegionId: tea.String("cn-hangzhou"),
AcceleratorId: tea.String(d.config.AcceleratorId),
NextToken: listListenerCertificatesNextToken,
MaxResults: tea.Int32(20),
}
listListenerCertificatesResp, err := d.sdkClient.ListListenerCertificates(listListenerCertificatesReq)
d.logger.Debug("sdk request 'ga.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ga.ListListenerCertificates': %w", err)
}
if listListenerCertificatesResp.Body.Certificates != nil {
for _, certificate := range listListenerCertificatesResp.Body.Certificates {
if tea.BoolValue(certificate.IsDefault) {
listenerDefaultCertificate = certificate
} else {
listenerAdditionalCertificates = append(listenerAdditionalCertificates, certificate)
}
}
}
if listListenerCertificatesResp.Body.NextToken == nil {
break
} else {
listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken
}
}
if d.config.Domain == "" {
// 未指定 SNI只需部署到监听器
if listenerDefaultCertificate != nil && tea.StringValue(listenerDefaultCertificate.CertificateId) == cloudCertId {
d.logger.Info("no need to update ga listener default certificate")
return nil
}
// 修改监听的属性
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updatelistener
updateListenerReq := &aliga.UpdateListenerRequest{
RegionId: tea.String("cn-hangzhou"),
ListenerId: tea.String(cloudListenerId),
Certificates: []*aliga.UpdateListenerRequestCertificates{{
Id: tea.String(cloudCertId),
}},
}
updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
d.logger.Debug("sdk request 'ga.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ga.UpdateListener': %w", err)
}
} else {
// 指定 SNI需部署到扩展域名
if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
return tea.StringValue(item.CertificateId) == cloudCertId
}) {
d.logger.Info("no need to update ga listener additional certificate")
return nil
}
if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
return tea.StringValue(item.Domain) == d.config.Domain
}) {
// 为监听替换扩展证书
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updateadditionalcertificatewithlistener
updateAdditionalCertificateWithListenerReq := &aliga.UpdateAdditionalCertificateWithListenerRequest{
RegionId: tea.String("cn-hangzhou"),
AcceleratorId: tea.String(cloudAcceleratorId),
ListenerId: tea.String(cloudListenerId),
CertificateId: tea.String(cloudCertId),
Domain: tea.String(d.config.Domain),
}
updateAdditionalCertificateWithListenerResp, err := d.sdkClient.UpdateAdditionalCertificateWithListener(updateAdditionalCertificateWithListenerReq)
d.logger.Debug("sdk request 'ga.UpdateAdditionalCertificateWithListener'", slog.Any("request", updateAdditionalCertificateWithListenerReq), slog.Any("response", updateAdditionalCertificateWithListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ga.UpdateAdditionalCertificateWithListener': %w", err)
}
} else {
// 为监听绑定扩展证书
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-associateadditionalcertificateswithlistener
associateAdditionalCertificatesWithListenerReq := &aliga.AssociateAdditionalCertificatesWithListenerRequest{
RegionId: tea.String("cn-hangzhou"),
AcceleratorId: tea.String(cloudAcceleratorId),
ListenerId: tea.String(cloudListenerId),
Certificates: []*aliga.AssociateAdditionalCertificatesWithListenerRequestCertificates{{
Id: tea.String(cloudCertId),
Domain: tea.String(d.config.Domain),
}},
}
associateAdditionalCertificatesWithListenerResp, err := d.sdkClient.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesWithListenerReq)
d.logger.Debug("sdk request 'ga.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesWithListenerReq), slog.Any("response", associateAdditionalCertificatesWithListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ga.AssociateAdditionalCertificatesWithListener': %w", err)
}
}
}
return nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*aliga.Client, error) {
// 接入点一览 https://api.aliyun.com/product/Ga
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String("ga.cn-hangzhou.aliyuncs.com"),
}
client, err := aliga.NewClient(config)
if err != nil {
return nil, err
}
return client, nil
}
func createSslUploader(accessKeyId, accessKeySecret string) (uploader.Uploader, error) {
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
Region: "cn-hangzhou",
})
return uploader, err
}

View File

@@ -0,0 +1,118 @@
package aliyunga_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fAccessKeySecret string
fAcceleratorId string
fListenerId string
fDomain string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNGA_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
flag.StringVar(&fAcceleratorId, argsPrefix+"ACCELERATORID", "", "")
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}
/*
Shell command to run this test:
go test -v ./aliyun_ga_test.go -args \
--CERTIMATE_DEPLOYER_ALIYUNGA_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_ALIYUNGA_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYSECRET="your-access-key-secret" \
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCELERATORID="your-ga-accelerator-id" \
--CERTIMATE_DEPLOYER_ALIYUNGA_LISTENERID="your-ga-listener-id" \
--CERTIMATE_DEPLOYER_ALIYUNGA_DOMAIN="your-ga-sni-domain"
*/
func TestDeploy(t *testing.T) {
flag.Parse()
t.Run("Deploy_ToAccelerator", 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("ACCELERATORID: %v", fAcceleratorId),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
ResourceType: provider.RESOURCE_TYPE_ACCELERATOR,
AcceleratorId: fAcceleratorId,
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)
})
t.Run("Deploy_ToListener", 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("LISTENERID: %v", fListenerId),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
ResourceType: provider.RESOURCE_TYPE_LISTENER,
ListenerId: fListenerId,
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)
})
}

View File

@@ -0,0 +1,10 @@
package aliyunga
type ResourceType string
const (
// 资源类型:部署到指定全球加速器。
RESOURCE_TYPE_ACCELERATOR = ResourceType("accelerator")
// 资源类型:部署到指定监听器。
RESOURCE_TYPE_LISTENER = ResourceType("listener")
)

View File

@@ -1,4 +1,4 @@
package aliyunlive
package aliyunlive
import (
"context"
@@ -10,7 +10,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
alilive "github.com/alibabacloud-go/live-20161101/client"
"github.com/alibabacloud-go/tea/tea"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -41,7 +40,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -60,7 +59,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 要求的泛域名格式
domain := strings.TrimPrefix(d.config.Domain, "*")
@@ -71,13 +70,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
SSLProtocol: tea.String("on"),
SSLPub: tea.String(certPem),
SSLPri: tea.String(privkeyPem),
SSLPub: tea.String(certPEM),
SSLPri: tea.String(privkeyPEM),
}
setLiveDomainSSLCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainSSLCertificateReq)
d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainSSLCertificateReq), slog.Any("response", setLiveDomainSSLCertificateResp))
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)
}
return &deployer.DeployResult{}, nil

View File

@@ -1,4 +1,4 @@
package aliyunlive_test
package aliyunlive_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunnlb
package aliyunnlb
import (
"context"
@@ -10,7 +10,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
alinlb "github.com/alibabacloud-go/nlb-20220430/v2/client"
"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/uploader"
@@ -50,12 +49,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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)
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{
@@ -76,11 +75,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -98,7 +97,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
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
@@ -117,7 +116,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
getLoadBalancerAttributeResp, err := d.sdkClient.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
d.logger.Debug("sdk request 'nlb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
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)
}
// 查询 TCPSSL 监听列表
@@ -126,6 +125,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
listListenersLimit := int32(100)
var listListenersToken *string = nil
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenersReq := &alinlb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
@@ -135,12 +140,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'nlb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
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 {
for _, listener := range listListenersResp.Body.Listeners {
listenerIds = append(listenerIds, *listener.ListenerId)
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
}
}
@@ -159,8 +164,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
var errs []error
for _, listenerId := range listenerIds {
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -194,7 +204,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
getListenerAttributeResp, err := d.sdkClient.GetListenerAttribute(getListenerAttributeReq)
d.logger.Debug("sdk request 'nlb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
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)
}
// 修改监听的属性
@@ -206,7 +216,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
updateListenerAttributeResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
d.logger.Debug("sdk request 'nlb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
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)
}
return nil
@@ -240,7 +250,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
// 阿里云 CAS 服务接入点是独立于 NLB 服务的
// 国内版固定接入点:华东一杭州
// 国际版固定接入点:亚太东南一新加坡
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
if !strings.HasPrefix(casRegion, "cn-") {
casRegion = "ap-southeast-1"
} else {
casRegion = "cn-hangzhou"

View File

@@ -1,4 +1,4 @@
package aliyunnlb_test
package aliyunnlb_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunnlb
package aliyunnlb
type ResourceType string

View File

@@ -1,4 +1,4 @@
package aliyunoss
package aliyunoss
import (
"context"
@@ -7,7 +7,6 @@ import (
"log/slog"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -40,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -59,7 +58,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 == "" {
return nil, errors.New("config `bucket` is required")
}
@@ -72,15 +71,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
putBucketCnameWithCertificateReq := oss.PutBucketCname{
Cname: d.config.Domain,
CertificateConfiguration: &oss.CertificateConfiguration{
Certificate: certPem,
PrivateKey: privkeyPem,
Certificate: certPEM,
PrivateKey: privkeyPEM,
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 {
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

View File

@@ -1,4 +1,4 @@
package aliyunoss_test
package aliyunoss_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunvod
package aliyunvod
import (
"context"
@@ -9,7 +9,6 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
alivod "github.com/alibabacloud-go/vod-20170321/v4/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
)
@@ -40,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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{
@@ -59,7 +58,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{
@@ -67,13 +66,13 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
SSLProtocol: tea.String("on"),
SSLPub: tea.String(certPem),
SSLPri: tea.String(privkeyPem),
SSLPub: tea.String(certPEM),
SSLPri: tea.String(privkeyPEM),
}
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq)
d.logger.Debug("sdk request 'live.SetVodDomainSSLCertificate'", slog.Any("request", setVodDomainSSLCertificateReq), slog.Any("response", setVodDomainSSLCertificateResp))
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)
}
return &deployer.DeployResult{}, nil

View File

@@ -1,4 +1,4 @@
package aliyunvod_test
package aliyunvod_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package aliyunwaf
package aliyunwaf
import (
"context"
@@ -10,12 +10,11 @@ import (
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
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/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
)
type DeployerConfig struct {
@@ -49,12 +48,12 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret, config.Region)
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)
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{
@@ -75,29 +74,29 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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 == "" {
return nil, errors.New("config `instanceId` is required")
}
switch d.config.ServiceVersion {
case "3", "3.0":
if err := d.deployToWAF3(ctx, certPem, privkeyPem); err != nil {
if err := d.deployToWAF3(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
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
}
func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, privkeyPem string) error {
func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPEM string, privkeyPEM string) error {
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
return fmt.Errorf("failed to upload certificate file: %w", err)
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
@@ -114,7 +113,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri
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 xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDefaultHttps'")
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDefaultHttps': %w", err)
}
// 修改默认 SSL/TLS 设置
@@ -133,7 +132,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri
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 xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDefaultHttps'")
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDefaultHttps': %w", err)
}
} else {
// 指定接入域名
@@ -148,7 +147,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri
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 xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDomainDetail'")
return fmt.Errorf("failed to execute sdk request 'waf.DescribeDomainDetail': %w", err)
}
// 修改 CNAME 接入资源
@@ -164,7 +163,7 @@ func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, pri
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 xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDomain'")
return fmt.Errorf("failed to execute sdk request 'waf.ModifyDomain': %w", err)
}
}
@@ -193,7 +192,7 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
// 阿里云 CAS 服务接入点是独立于 WAF 服务的
// 国内版固定接入点:华东一杭州
// 国际版固定接入点:亚太东南一新加坡
if casRegion != "" && !strings.HasPrefix(casRegion, "cn-") {
if !strings.HasPrefix(casRegion, "cn-") {
casRegion = "ap-southeast-1"
} else {
casRegion = "cn-hangzhou"

View File

@@ -1,4 +1,4 @@
package aliyunwaf_test
package aliyunwaf_test
import (
"context"

View File

@@ -1,14 +1,19 @@
package awsacm
package awsacm
import (
"context"
"fmt"
"log/slog"
xerrors "github.com/pkg/errors"
aws "github.com/aws/aws-sdk-go-v2/aws"
awscfg "github.com/aws/aws-sdk-go-v2/config"
awscred "github.com/aws/aws-sdk-go-v2/credentials"
awsacm "github.com/aws/aws-sdk-go-v2/service/acm"
"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/aws-acm"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
)
type DeployerConfig struct {
@@ -18,11 +23,15 @@ type DeployerConfig struct {
SecretAccessKey string `json:"secretAccessKey"`
// AWS 区域。
Region string `json:"region"`
// ACM 证书 ARN。
// 选填。零值时表示新建证书;否则表示更新证书。
CertificateArn string `json:"certificateArn,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *awsacm.Client
sslUploader uploader.Uploader
}
@@ -33,18 +42,24 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
})
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{
config: config,
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
@@ -59,14 +74,49 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 ACM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
if d.config.CertificateArn == "" {
// 上传证书到 ACM
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))
}
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
// 提取服务器证书
serverCertPEM, intermediaCertPEM, err := certutil.ExtractCertificatesFromPEM(certPEM)
if err != nil {
return nil, fmt.Errorf("failed to extract certs: %w", err)
}
// 导入证书
// REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html
importCertificateReq := &awsacm.ImportCertificateInput{
CertificateArn: aws.String(d.config.CertificateArn),
Certificate: ([]byte)(serverCertPEM),
CertificateChain: ([]byte)(intermediaCertPEM),
PrivateKey: ([]byte)(privkeyPEM),
}
importCertificateResp, err := d.sdkClient.ImportCertificate(context.TODO(), importCertificateReq)
d.logger.Debug("sdk request 'acm.ImportCertificate'", slog.Any("request", importCertificateReq), slog.Any("response", importCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'acm.ImportCertificate': %w", err)
}
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*awsacm.Client, error) {
cfg, err := awscfg.LoadDefaultConfig(context.TODO())
if err != nil {
return nil, err
}
client := awsacm.NewFromConfig(cfg, func(o *awsacm.Options) {
o.Region = region
o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
})
return client, nil
}

View File

@@ -1,8 +1,9 @@
package awscloudfront
package awscloudfront
import (
"context"
"errors"
"fmt"
"log/slog"
aws "github.com/aws/aws-sdk-go-v2/aws"
@@ -10,7 +11,6 @@ import (
awscred "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
"github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
@@ -44,7 +44,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
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{
@@ -53,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
Region: config.Region,
})
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{
@@ -74,15 +74,15 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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.DistributionId == "" {
return nil, errors.New("config `distribuitionId` is required")
}
// 上传证书到 ACM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -95,7 +95,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
getDistributionConfigResp, err := d.sdkClient.GetDistributionConfig(context.TODO(), getDistributionConfigReq)
d.logger.Debug("sdk request 'cloudfront.GetDistributionConfig'", slog.Any("request", getDistributionConfigReq), slog.Any("response", getDistributionConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cloudfront.GetDistributionConfig'")
return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.GetDistributionConfig': %w", err)
}
// 更新分配配置
@@ -113,7 +113,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
updateDistributionResp, err := d.sdkClient.UpdateDistribution(context.TODO(), updateDistributionReq)
d.logger.Debug("sdk request 'cloudfront.UpdateDistribution'", slog.Any("request", updateDistributionReq), slog.Any("response", updateDistributionResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cloudfront.UpdateDistribution'")
return nil, fmt.Errorf("failed to execute sdk request 'cloudfront.UpdateDistribution': %w", err)
}
return &deployer.DeployResult{}, nil

View File

@@ -1,4 +1,4 @@
package awscloudfront_test
package awscloudfront_test
import (
"context"

View File

@@ -1,14 +1,23 @@
package azurekeyvault
package azurekeyvault
import (
"context"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"log/slog"
xerrors "github.com/pkg/errors"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates"
"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/azure-keyvault"
azcommon "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/common"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
)
type DeployerConfig struct {
@@ -22,11 +31,15 @@ type DeployerConfig struct {
CloudName string `json:"cloudName,omitempty"`
// Key Vault 名称。
KeyVaultName string `json:"keyvaultName"`
// Key Vault 证书名称。
// 选填。零值时表示新建证书;否则表示更新证书。
CertificateName string `json:"certificateName,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *azcertificates.Client
sslUploader uploader.Uploader
}
@@ -37,6 +50,11 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.TenantId, config.ClientId, config.ClientSecret, config.CloudName, config.KeyVaultName)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
TenantId: config.TenantId,
ClientId: config.ClientId,
@@ -45,12 +63,13 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
KeyVaultName: config.KeyVaultName,
})
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{
config: config,
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
@@ -65,14 +84,94 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 KeyVault
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
// 解析证书内容
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
return nil, err
}
// 转换证书格式
certPFX, err := certutil.TransformCertificateFromPEMToPFX(certPEM, privkeyPEM, "")
if err != nil {
return nil, fmt.Errorf("failed to transform certificate from PEM to PFX: %w", err)
}
if d.config.CertificateName == "" {
// 上传证书到 KeyVault
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))
}
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
// 获取证书
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate
getCertificateResp, err := d.sdkClient.GetCertificate(context.TODO(), d.config.CertificateName, "", nil)
d.logger.Debug("sdk request 'keyvault.GetCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("response", getCertificateResp))
if err != nil {
var respErr *azcore.ResponseError
if !errors.As(err, &respErr) || (respErr.ErrorCode != "ResourceNotFound" && respErr.ErrorCode != "CertificateNotFound") {
return nil, fmt.Errorf("failed to execute sdk request 'keyvault.GetCertificate': %w", err)
}
} else {
oldCertX509, err := x509.ParseCertificate(getCertificateResp.CER)
if err == nil {
if certutil.EqualCertificate(certX509, oldCertX509) {
return &deployer.DeployResult{}, nil
}
}
}
// 导入证书
// REF: https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/import-certificate/import-certificate
importCertificateParams := azcertificates.ImportCertificateParameters{
Base64EncodedCertificate: to.Ptr(base64.StdEncoding.EncodeToString(certPFX)),
CertificatePolicy: &azcertificates.CertificatePolicy{
SecretProperties: &azcertificates.SecretProperties{
ContentType: to.Ptr("application/x-pkcs12"),
},
},
Tags: map[string]*string{
"certimate/cert-cn": to.Ptr(certX509.Subject.CommonName),
"certimate/cert-sn": to.Ptr(certX509.SerialNumber.Text(16)),
},
}
importCertificateResp, err := d.sdkClient.ImportCertificate(context.TODO(), d.config.CertificateName, importCertificateParams, nil)
d.logger.Debug("sdk request 'keyvault.ImportCertificate'", slog.String("request.certificateName", d.config.CertificateName), slog.Any("request.parameters", importCertificateParams), slog.Any("response", importCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'keyvault.ImportCertificate': %w", err)
}
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(tenantId, clientId, clientSecret, cloudName, keyvaultName string) (*azcertificates.Client, error) {
env, err := azcommon.GetCloudEnvironmentConfiguration(cloudName)
if err != nil {
return nil, err
}
clientOptions := azcore.ClientOptions{Cloud: env}
credential, err := azidentity.NewClientSecretCredential(tenantId, clientId, clientSecret,
&azidentity.ClientSecretCredentialOptions{ClientOptions: clientOptions})
if err != nil {
return nil, err
}
endpoint := fmt.Sprintf("https://%s.vault.azure.net", keyvaultName)
if azcommon.IsEnvironmentGovernment(cloudName) {
endpoint = fmt.Sprintf("https://%s.vault.usgovcloudapi.net", keyvaultName)
} else if azcommon.IsEnvironmentChina(cloudName) {
endpoint = fmt.Sprintf("https://%s.vault.azure.cn", keyvaultName)
}
client, err := azcertificates.NewClient(endpoint, credential, nil)
if err != nil {
return nil, err
}
return client, nil
}

View File

@@ -1,4 +1,4 @@
package baiducloudappblb
package baiducloudappblb
import (
"context"
@@ -10,12 +10,11 @@ import (
bceappblb "github.com/baidubce/bce-sdk-go/services/appblb"
"github.com/google/uuid"
xerrors "github.com/pkg/errors"
"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/baiducloud-cert"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
)
type DeployerConfig struct {
@@ -54,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
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{
@@ -62,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
SecretAccessKey: config.SecretAccessKey,
})
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{
@@ -82,11 +81,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -104,7 +103,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
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
@@ -120,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeLoadBalancerDetailResp, err := d.sdkClient.DescribeLoadBalancerDetail(d.config.LoadbalancerId)
d.logger.Debug("sdk request 'appblb.DescribeLoadBalancerAttribute'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("response", describeLoadBalancerDetailResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeLoadBalancerDetail'")
return fmt.Errorf("failed to execute sdk request 'appblb.DescribeLoadBalancerDetail': %w", err)
}
// 获取全部 HTTPS/SSL 监听端口
@@ -153,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
var errs []error
for _, listener := range listeners {
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -182,7 +187,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
describeAppAllListenersResp, err := d.sdkClient.DescribeAppAllListeners(d.config.LoadbalancerId, describeAppAllListenersRequest)
d.logger.Debug("sdk request 'appblb.DescribeAppAllListeners'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("request", describeAppAllListenersRequest), slog.Any("response", describeAppAllListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeAppAllListeners'")
return fmt.Errorf("failed to execute sdk request 'appblb.DescribeAppAllListeners': %w", err)
}
// 获取全部 HTTPS/SSL 监听端口
@@ -210,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
var errs []error
for _, listener := range listeners {
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -230,7 +241,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
case "SSL":
return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
default:
return fmt.Errorf("unsupported listener type: %s", cloudListenerType)
return fmt.Errorf("unsupported listener type '%s'", cloudListenerType)
}
}
@@ -244,7 +255,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
describeAppHTTPSListenersResp, err := d.sdkClient.DescribeAppHTTPSListeners(cloudLoadbalancerId, describeAppHTTPSListenersReq)
d.logger.Debug("sdk request 'appblb.DescribeAppHTTPSListeners'", slog.String("blbId", cloudLoadbalancerId), slog.Any("request", describeAppHTTPSListenersReq), slog.Any("response", describeAppHTTPSListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.DescribeAppHTTPSListeners'")
return fmt.Errorf("failed to execute sdk request 'appblb.DescribeAppHTTPSListeners': %w", err)
} else if len(describeAppHTTPSListenersResp.ListenerList) == 0 {
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
}
@@ -257,12 +268,13 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
Scheduler: describeAppHTTPSListenersResp.ListenerList[0].Scheduler,
CertIds: []string{cloudCertId},
}
err := d.sdkClient.UpdateAppHTTPSListener(cloudLoadbalancerId, updateAppHTTPSListenerReq)
d.logger.Debug("sdk request 'appblb.UpdateAppHTTPSListener'", slog.Any("request", updateAppHTTPSListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppHTTPSListener'")
return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppHTTPSListener': %w", err)
}
} else {
// 指定 SNI需部署到扩展域名
@@ -272,6 +284,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
Scheduler: describeAppHTTPSListenersResp.ListenerList[0].Scheduler,
AdditionalCertDomains: sliceutil.Map(describeAppHTTPSListenersResp.ListenerList[0].AdditionalCertDomains, func(domain bceappblb.AdditionalCertDomainsModel) bceappblb.AdditionalCertDomainsModel {
if domain.Host == d.config.Domain {
return bceappblb.AdditionalCertDomainsModel{
@@ -289,7 +302,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
err := d.sdkClient.UpdateAppHTTPSListener(cloudLoadbalancerId, updateAppHTTPSListenerReq)
d.logger.Debug("sdk request 'appblb.UpdateAppHTTPSListener'", slog.Any("request", updateAppHTTPSListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppHTTPSListener'")
return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppHTTPSListener': %w", err)
}
}
@@ -307,7 +320,7 @@ func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, clo
err := d.sdkClient.UpdateAppSSLListener(cloudLoadbalancerId, updateAppSSLListenerReq)
d.logger.Debug("sdk request 'appblb.UpdateAppSSLListener'", slog.Any("request", updateAppSSLListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'appblb.UpdateAppSSLListener'")
return fmt.Errorf("failed to execute sdk request 'appblb.UpdateAppSSLListener': %w", err)
}
return nil

View File

@@ -1,4 +1,4 @@
package baiducloudappblb_test
package baiducloudappblb_test
import (
"context"

View File

@@ -1,4 +1,4 @@
package baiducloudappblb
package baiducloudappblb
type ResourceType string

View File

@@ -1,4 +1,4 @@
package baiducloudblb
package baiducloudblb
import (
"context"
@@ -10,12 +10,11 @@ import (
bceblb "github.com/baidubce/bce-sdk-go/services/blb"
"github.com/google/uuid"
xerrors "github.com/pkg/errors"
"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/baiducloud-cert"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
)
type DeployerConfig struct {
@@ -54,7 +53,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
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{
@@ -62,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
SecretAccessKey: config.SecretAccessKey,
})
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{
@@ -82,11 +81,11 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
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
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
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))
}
@@ -104,7 +103,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
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
@@ -120,7 +119,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeLoadBalancerDetailResp, err := d.sdkClient.DescribeLoadBalancerDetail(d.config.LoadbalancerId)
d.logger.Debug("sdk request 'blb.DescribeLoadBalancerAttribute'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("response", describeLoadBalancerDetailResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeLoadBalancerDetail'")
return fmt.Errorf("failed to execute sdk request 'blb.DescribeLoadBalancerDetail': %w", err)
}
// 获取全部 HTTPS/SSL 监听端口
@@ -153,8 +152,14 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
var errs []error
for _, listener := range listeners {
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -182,7 +187,7 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
describeAllListenersResp, err := d.sdkClient.DescribeAllListeners(d.config.LoadbalancerId, describeAllListenersRequest)
d.logger.Debug("sdk request 'blb.DescribeAllListeners'", slog.String("blbId", d.config.LoadbalancerId), slog.Any("request", describeAllListenersRequest), slog.Any("response", describeAllListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeAllListeners'")
return fmt.Errorf("failed to execute sdk request 'blb.DescribeAllListeners': %w", err)
}
// 获取全部 HTTPS/SSL 监听端口
@@ -210,8 +215,14 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
var errs []error
for _, listener := range listeners {
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listener.Type, listener.Port, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
@@ -230,7 +241,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
case "SSL":
return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
default:
return fmt.Errorf("unsupported listener type: %s", cloudListenerType)
return fmt.Errorf("unsupported listener type '%s'", cloudListenerType)
}
}
@@ -244,7 +255,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
describeHTTPSListenersResp, err := d.sdkClient.DescribeHTTPSListeners(cloudLoadbalancerId, describeHTTPSListenersReq)
d.logger.Debug("sdk request 'blb.DescribeHTTPSListeners'", slog.String("blbId", cloudLoadbalancerId), slog.Any("request", describeHTTPSListenersReq), slog.Any("response", describeHTTPSListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.DescribeHTTPSListeners'")
return fmt.Errorf("failed to execute sdk request 'blb.DescribeHTTPSListeners': %w", err)
} else if len(describeHTTPSListenersResp.ListenerList) == 0 {
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
}
@@ -262,7 +273,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
err := d.sdkClient.UpdateHTTPSListener(cloudLoadbalancerId, updateHTTPSListenerReq)
d.logger.Debug("sdk request 'blb.UpdateHTTPSListener'", slog.Any("request", updateHTTPSListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateHTTPSListener'")
return fmt.Errorf("failed to execute sdk request 'blb.UpdateHTTPSListener': %w", err)
}
} else {
// 指定 SNI需部署到扩展域名
@@ -289,7 +300,7 @@ func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, c
err := d.sdkClient.UpdateHTTPSListener(cloudLoadbalancerId, updateHTTPSListenerReq)
d.logger.Debug("sdk request 'blb.UpdateHTTPSListener'", slog.Any("request", updateHTTPSListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateHTTPSListener'")
return fmt.Errorf("failed to execute sdk request 'blb.UpdateHTTPSListener': %w", err)
}
}
@@ -307,7 +318,7 @@ func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, clo
err := d.sdkClient.UpdateSSLListener(cloudLoadbalancerId, updateSSLListenerReq)
d.logger.Debug("sdk request 'blb.UpdateSSLListener'", slog.Any("request", updateSSLListenerReq))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'blb.UpdateSSLListener'")
return fmt.Errorf("failed to execute sdk request 'blb.UpdateSSLListener': %w", err)
}
return nil

View File

@@ -1,4 +1,4 @@
package baiducloudblb_test
package baiducloudblb_test
import (
"context"

Some files were not shown because too many files have changed in this diff Show More