Compare commits

...

183 Commits
next ... v0.3.8

Author SHA1 Message Date
Yoan.liu
88b90986b1 update to version v0.3.8 2025-04-13 20:55:33 +08:00
Yoan.liu
44a6190e17 resolve build error 2025-04-13 09:14:08 +08:00
Yoan.liu
4475ed0dea resolve build error 2025-04-13 08:54:05 +08:00
Yoan.liu
6a23da3de3 Merge pull request #596 from redzl/redzl-patch-1
bugfix: tencent cloud ecdn deploy error
2025-04-13 08:24:43 +08:00
Yoan.liu
0f1d5a7730 Merge pull request #604 from banto6/main
feat(notify): add mattermost
2025-04-13 08:24:26 +08:00
Yoan.liu
5b4c3bb668 Merge branch 'main' into main 2025-04-13 08:24:16 +08:00
Yoan.liu
ad49f9d788 Merge pull request #607 from imlonghao/feat/pushover
feat: support pushover as notification
2025-04-13 08:18:59 +08:00
Yoan.liu
397ceefa02 Merge branch 'main' into feat/pushover 2025-04-13 08:18:47 +08:00
Yoan.liu
e11b1ca4e8 Merge pull request #597 from fudiwei/feat/providers
new providers
2025-04-13 08:14:50 +08:00
Yoan.liu
8e983e7286 Merge pull request #587 from fudiwei/bugfix
bugfix
2025-04-13 08:13:06 +08:00
Fu Diwei
f970ae7529 feat: add wangsu cdnpro deployer 2025-04-12 21:43:21 +08:00
Fu Diwei
b0973b5ca8 refactor: clean code 2025-04-12 20:54:02 +08:00
banto
4784bf9dba feat: add channelId tooltip 2025-04-12 20:01:03 +08:00
imlonghao
6b8dbf5235 feat: support pushover as notification 2025-04-12 13:05:37 +08:00
banto
48f698e84b style: fix code style 2025-04-12 12:45:03 +08:00
banto
ec0cdf8b96 feat(notify): add mattermost 2025-04-11 22:55:47 +08:00
Fu Diwei
2a6cc01eed feat(ui): adjust table scroll width in Dashboard 2025-04-10 21:57:22 +08:00
Fu Diwei
acc1365101 Merge branch 'feat/providers' of https://github.com/fudiwei/certimate into feat/providers 2025-04-09 23:12:52 +08:00
Fu Diwei
c5409c78ba refactor: edgio api sdk 2025-04-09 23:12:11 +08:00
RHQYZ
b97de6c06b Merge branch 'usual2970:main' into feat/providers 2025-04-09 22:56:43 +08:00
RHQYZ
4e3f499d76 chore: github issue templates 2025-04-09 10:55:53 +08:00
Fu Diwei
3cebe51796 feat: add rainyun rcdn deployer 2025-04-08 21:53:16 +08:00
Fu Diwei
25bd17dc6e feat: add rainyun ssl center uploader 2025-04-08 21:53:05 +08:00
redzl
2525f54dc3 解决腾讯云ECDN部署报错的问题
ECDN部署的时候报错:failed to execute sdk request 'ssl.DeployCertificateInstance':[TencentCloudSDKError] Code=FailedOperation.CertificateHostResourceTypeInvalid, Message=云资源类型无效。
经排查'ssl.DeployCertificateInstance接口的ResourceType不支持ecdn类型,ecdn和cdn都需要传入cdn
2025-04-08 18:06:51 +08:00
Fu Diwei
2127bb7e69 Merge branch 'feat/providers' of https://github.com/fudiwei/certimate into feat/providers 2025-04-08 16:47:49 +08:00
Fu Diwei
ed6d74f1ba feat(ui): builtin providers tag 2025-04-08 16:44:10 +08:00
Fu Diwei
02dd11f196 chore(ui): improve i18n 2025-04-08 10:19:42 +08:00
Fu Diwei
37b9ae30e2 fix: #595 2025-04-08 09:41:16 +08:00
Fu Diwei
0463dbcc75 Merge branch 'bugfix' of https://github.com/fudiwei/certimate into bugfix 2025-04-07 15:32:12 +08:00
Fu Diwei
111ef97d9c fix: migration error 2025-04-07 15:31:20 +08:00
RHQYZ
e8e854e392 Merge branch 'usual2970:main' into bugfix 2025-04-07 12:42:22 +08:00
Yoan.liu
ff43b9ab3e Update version.ts 2025-04-07 09:26:19 +08:00
Fu Diwei
47c4ba9dd6 feat(ui): workflow runs deleting warning 2025-04-05 21:23:55 +08:00
Fu Diwei
6ff738144a fix: #585 #586 2025-04-03 20:33:58 +08:00
Fu Diwei
26028bb1eb chore(ui): improve i18n 2025-04-03 20:30:44 +08:00
Yoan.liu
eb4d5ddfd5 Merge pull request #573 from fudiwei/main
Support configuring independent CA for each workflow
2025-04-03 17:42:42 +08:00
Yoan.liu
093ee006e4 Merge pull request #578 from fudiwei/feat/providers
Support cloudflare zone api token
2025-04-03 17:42:23 +08:00
Yoan.liu
9f8aa15af8 Merge pull request #579 from catfishlty/feat/gotify
feat(notify): add gotify
2025-04-03 17:42:00 +08:00
Yoan.liu
74d66a0131 Merge pull request #583 from catfishlty/feat/pushplus
feat(notify): add pushplus
2025-04-03 17:41:46 +08:00
catfishlty
626a86dea7 fix(notify): optimize gotify code and close unreleased resources. 2025-04-03 09:47:19 +08:00
catfishlty
9ab029a296 fix(notify): optimize pushplus code and close unreleased resources. 2025-04-03 09:34:23 +08:00
Fu Diwei
8e1a81ae53 chore: improve i18n 2025-04-02 21:57:33 +08:00
Fu Diwei
d76e1a3204 refactor: clean code 2025-04-02 21:46:27 +08:00
catfishlty
b585782007 feat(notify): add pushplus 2025-04-02 15:04:41 +08:00
catfishlty
2d198bcef7 fix(notify): add missing config for gotify 2025-04-02 15:00:46 +08:00
Fu Diwei
0edcd9174f feat(ui): download workflow run logs 2025-04-02 13:40:55 +08:00
Fu Diwei
daa5b44f8e refactor(ui): clean code 2025-04-02 12:54:51 +08:00
Fu Diwei
949660bc01 feat(ui): add AccessEditDrawer component 2025-04-02 11:02:09 +08:00
Fu Diwei
899a0b75b0 feat(ui): improve access provider tags appearance 2025-04-01 21:23:51 +08:00
Fu Diwei
8cdb2afa69 refactor: clean code 2025-04-01 20:44:45 +08:00
catfishlty
00ec2ce33e feat(notify): add gotify 2025-04-01 10:53:41 +08:00
Fu Diwei
2f7fd95684 feat: cloudflare zone api token 2025-03-31 21:13:07 +08:00
Fu Diwei
55b1794004 chore: improve i18n 2025-03-31 20:03:08 +08:00
Fu Diwei
e20972d4e7 chore: improve i18n 2025-03-31 20:00:03 +08:00
Fu Diwei
749d727f50 fix: could not obtain ecc certificates from sslcom 2025-03-31 10:24:35 +08:00
Fu Diwei
9b524728c0 update README 2025-03-30 22:46:33 +08:00
Fu Diwei
f81b4b9680 feat(ui): hide notification channel entry in AcessList for now 2025-03-30 22:33:05 +08:00
Fu Diwei
d2eaea7a44 feat: add buypass ca 2025-03-30 22:15:21 +08:00
Fu Diwei
f77c2dae23 feat: add ssl.com ca 2025-03-30 22:15:21 +08:00
Fu Diwei
a72737fdd5 feat(ui): different provider range of accesses in AccessList 2025-03-30 22:15:21 +08:00
Fu Diwei
4ab6b72e6f feat(ui): different provider range of accesses in AccessForm 2025-03-30 22:15:08 +08:00
Fu Diwei
1468e74a6c fix: ari 2025-03-30 14:02:43 +08:00
Fu Diwei
09b5a21af1 feat: make the builtin providers access field non mandatory 2025-03-30 13:57:26 +08:00
Fu Diwei
6ad0d8e42f feat: support configuring independent ca in workflows 2025-03-30 13:57:26 +08:00
Fu Diwei
deb3b2f412 feat: manage ca authorizations 2025-03-30 13:57:21 +08:00
Yoan.liu
893391a3d1 Merge pull request #566 from fudiwei/main
enhance & bugfix
2025-03-29 20:01:13 +08:00
Fu Diwei
7503d52857 refactor: clean code 2025-03-27 20:39:06 +08:00
Fu Diwei
fb860981d6 fix: #568 2025-03-27 15:53:29 +08:00
Fu Diwei
f302c7fb74 feat: support replacing old certificate on deployment to baishan cdn 2025-03-27 14:14:34 +08:00
Fu Diwei
a8be2a77cf fix: #565 2025-03-27 14:14:27 +08:00
Fu Diwei
c2345e6118 style: format 2025-03-27 09:47:16 +08:00
Yoan.liu
539f8f3343 update to version v0.3.6 2025-03-26 21:51:13 +08:00
Yoan.liu
9a06c1e35b Merge pull request #558 from fudiwei/main
new providers & bugfix
2025-03-26 17:59:27 +08:00
Fu Diwei
382de0d6d6 refactor: clean code 2025-03-26 10:43:37 +08:00
Fu Diwei
4883b3bb88 feat(ui): make request error friendly 2025-03-26 10:18:27 +08:00
Fu Diwei
0a90523d61 fix(ui): login theme error in dark mode 2025-03-25 20:31:02 +08:00
Fu Diwei
fa63f2a838 feat: add tencentcloud-eo dns-01 applicant 2025-03-25 20:28:05 +08:00
Fu Diwei
fd8ac3ae37 feat(ui): allow select dns-01 provider on application 2025-03-25 19:52:09 +08:00
Fu Diwei
51c1b193e5 fix: #559 2025-03-25 19:41:55 +08:00
Fu Diwei
ee99bcf8a1 Merge branch 'main' of https://github.com/usual2970/certimate 2025-03-25 17:20:28 +08:00
Fu Diwei
324086ca49 chore: github issue templates 2025-03-25 17:19:28 +08:00
Fu Diwei
e9610eaede fix: #556 2025-03-25 16:17:35 +08:00
Fu Diwei
7d5c714211 feat: improve i18n 2025-03-25 13:54:00 +08:00
Fu Diwei
24e275fdb3 feat: add volcengine certcenter deployer 2025-03-25 13:54:00 +08:00
Fu Diwei
597b9d0e17 feat: add huaweicloud scm deployer 2025-03-25 13:54:00 +08:00
Fu Diwei
4d710a1aaf feat: add baiducloud cert deployer 2025-03-25 13:54:00 +08:00
Fu Diwei
5de033814b feat: add baiducloud appblb deployer 2025-03-25 13:54:00 +08:00
Fu Diwei
aaec840d8c feat: add baiducloud blb deployer 2025-03-25 13:53:54 +08:00
Fu Diwei
e579cf6ceb feat: add baiducloud cert uploader 2025-03-25 13:53:39 +08:00
Yoan.liu
798e72f663 Merge pull request #543 from fudiwei/main
new providers & bugfix
2025-03-25 09:23:54 +08:00
Fu Diwei
e79d862256 chore: github issue templates 2025-03-24 20:55:06 +08:00
Fu Diwei
53133db456 refactor: clean code 2025-03-24 12:37:20 +08:00
Fu Diwei
39f8484b2a fix: #544 2025-03-24 12:35:30 +08:00
Fu Diwei
892256c0b9 fix: #544 2025-03-24 11:25:02 +08:00
Fu Diwei
0545945697 refactor: clean code 2025-03-24 10:31:41 +08:00
Fu Diwei
ad0125fe0d feat: add vercel dns-01 applicant 2025-03-23 22:42:59 +08:00
Fu Diwei
fb325b5447 feat: add desec dns-01 applicant 2025-03-23 22:42:59 +08:00
Fu Diwei
56ff9e6344 feat: add porkbun dns-01 applicant 2025-03-23 22:42:59 +08:00
Fu Diwei
74b431481d feat: add volcengine alb deployer 2025-03-23 22:42:51 +08:00
Fu Diwei
12102ef641 refactor: clean code 2025-03-23 10:19:24 +08:00
Fu Diwei
445541c38f fix: #548 2025-03-23 00:42:58 +08:00
Fu Diwei
820f03e162 feat: support wildcard domain on deployment to aliyun fc 2025-03-22 20:54:54 +08:00
Fu Diwei
516a958c66 fix: #544 2025-03-22 17:51:27 +08:00
Yoan.liu
7da101d5b7 update to version v0.3.5 2025-03-21 20:57:57 +08:00
Fu Diwei
9667f3309b fix: #542 2025-03-21 20:13:05 +08:00
Fu Diwei
82735f3c02 refactor: clean code 2025-03-21 19:55:29 +08:00
Fu Diwei
752acb591f refactor: clean code 2025-03-21 18:11:17 +08:00
Yoan.liu
43d851c7ef Merge pull request #541 from fudiwei/main
new providers & bugfix
2025-03-21 07:00:57 +08:00
Fu Diwei
95e1fc6b5f fix: #539 2025-03-21 01:30:35 +08:00
Fu Diwei
a8a12a3b91 chore(deps): upgrade gomod dependencies 2025-03-20 23:55:25 +08:00
Fu Diwei
63a95723ac chore(deps): upgrade npm dependencies 2025-03-20 23:55:20 +08:00
Fu Diwei
02f806ab99 feat: preset script for backup files on deployment to local and ssh 2025-03-20 23:36:20 +08:00
Fu Diwei
da6526d5fa feat: add dynv6 dns-01 applicant 2025-03-20 23:36:20 +08:00
Fu Diwei
347d166250 feat: add aliyun cas, tencentcloud ssl, aws acm, azure keyvault deployer 2025-03-20 23:36:19 +08:00
Fu Diwei
ef22d9d07b feat: add qiniu kodo deployer 2025-03-20 23:36:19 +08:00
Fu Diwei
e4fd1e78f5 feat: add upyun file deployer 2025-03-20 23:36:19 +08:00
Fu Diwei
4acbbf6e13 feat: add upyun cdn deployer 2025-03-20 23:36:19 +08:00
Fu Diwei
16f20dc01d feat: add upyun ssl uploader 2025-03-20 23:36:19 +08:00
Fu Diwei
8e4b3d12bd fix: #527 2025-03-20 23:36:19 +08:00
Fu Diwei
09b1bf6e2d fix: #523 2025-03-20 23:36:19 +08:00
Fu Diwei
7e4aa24459 fix: #539 2025-03-20 23:36:13 +08:00
Yoan.liu
7c94999efc Merge pull request #525 from fudiwei/main
new workflow logging
2025-03-20 21:26:39 +08:00
Fu Diwei
e27d4f11ee feat: auto cleanup workflow history runs and expired certificates 2025-03-19 17:12:24 +08:00
Fu Diwei
914c5b4870 refactor: clean code 2025-03-19 10:30:12 +08:00
Fu Diwei
882f802585 feat(ui): enhance workflow logs display 2025-03-19 10:09:30 +08:00
Yoan.liu
5579780b12 Merge pull request #532 from Anbool/main
修复白山云API 400209错误
2025-03-19 09:16:12 +08:00
Fu Diwei
fd6e41c566 feat(ui): workflow logs 2025-03-18 20:02:39 +08:00
RHQYZ
984d2a47b8 style: format code 2025-03-18 18:23:46 +08:00
root
92bae0c439 修复白山云API 400209错误 2025-03-18 16:48:19 +08:00
Fu Diwei
af5d7465a1 feat: adapt new logging to workflow node processors 2025-03-17 22:50:57 +08:00
Fu Diwei
b620052b88 feat: adapt new logging to uploader, deployer and notifier providers 2025-03-17 22:50:47 +08:00
Fu Diwei
c13a7a7873 feat: logging 2025-03-16 18:43:54 +08:00
Yoan.liu
65b199d392 update to version v0.3.4 2025-03-14 11:25:58 +08:00
Yoan.liu
dc0b86281e Merge pull request #517 from fudiwei/main
bugfix
2025-03-14 07:11:00 +08:00
Yoan.liu
b8796991b5 Merge pull request #518 from usual2970/hotfix/display
解决分支过多内容展示不完整的问题
2025-03-14 07:10:13 +08:00
Yoan.liu
83447fff62 fix the issue where content is not fully displayed when there are too many branches. 2025-03-13 17:22:34 +08:00
Fu Diwei
4a02c252d5 feat(ui): auto complete tencentcloud ssl deploy resource type 2025-03-13 16:20:03 +08:00
Fu Diwei
cb88df04b0 fix: #516 2025-03-13 16:03:35 +08:00
Yoan.liu
e888df2b9f Merge pull request #515 from fudiwei/main
enhance & bugfix
2025-03-12 21:44:12 +08:00
Fu Diwei
17af07e4bb feat: support sni on deployment to aliyun waf 2025-03-12 19:58:58 +08:00
Fu Diwei
d1aed36154 fix: #512 2025-03-12 19:36:14 +08:00
Fu Diwei
eb97f7a661 refactor: clean code 2025-03-12 16:56:02 +08:00
Yoan.liu
be822ccc93 update readme 2025-03-12 16:55:33 +08:00
Yoan.liu
e2b52eed61 Merge pull request #511 from LeoChen98/feat-rdp-preset
add feat: Windows RDP binding preset
2025-03-11 22:15:36 +08:00
Leo Chen
21717985ac add feat: Windows RDP binding preset 2025-03-11 21:38:22 +08:00
Yoan.liu
3c4ffee7d3 Update push_image.yml 2025-03-11 06:35:41 +08:00
Yoan.liu
3e1a457609 update to version v0.3.3 2025-03-10 21:33:06 +08:00
Yoan.liu
b28f0dc5e4 Merge pull request #504 from fudiwei/main
bugfix
2025-03-10 21:15:23 +08:00
Yoan.liu
29561ed75d Merge pull request #505 from usual2970/feat/image_tags
镜像增加大版本 tag
2025-03-10 21:14:47 +08:00
Yoan.liu
2e931d1f67 when tagging the image, also tag the major version 2025-03-10 16:33:28 +08:00
Fu Diwei
c907f22275 fix: wrong detection results of certificate key algorithm 2025-03-10 16:18:30 +08:00
Fu Diwei
19ccac5c05 build: set timezone in docker-compose 2025-03-10 15:22:25 +08:00
Fu Diwei
f9e3797cdd feat: default set autoRestart on deployment to 1panel or baotapanel 2025-03-10 15:13:41 +08:00
RHQYZ
a30379bfdb Merge branch 'usual2970:main' into main 2025-03-10 13:48:47 +08:00
Yoan.liu
dad1b4dfa6 update to version v0.3.2 2025-03-10 06:49:57 +08:00
Fu Diwei
643e09a4e6 fix: typo 2025-03-09 13:04:27 +08:00
Fu Diwei
56fc2d8b44 fix: invalid version checker 2025-03-09 12:57:01 +08:00
Yoan.liu
786f2f8678 Merge pull request #498 from usual2970/hotfix/workflow
fix the issue where the deployment node could not set the certificate…
2025-03-09 12:42:22 +08:00
Yoan.liu
ed689dba41 restore currentlength 2025-03-09 12:40:32 +08:00
Yoan.liu
f779117ed6 fix the issue where the deployment node could not set the certificate source. 2025-03-09 12:23:14 +08:00
Fu Diwei
c9e7e00f42 update README 2025-03-09 11:03:32 +08:00
Yoan.liu
6019945d83 Merge branch 'main' of github.com:usual2970/certimate 2025-03-09 07:22:13 +08:00
Yoan.liu
e0aed060aa update to version 0.3.1 2025-03-09 07:21:56 +08:00
Yoan.liu
1b03be774d Merge pull request #494 from fudiwei/main
enhance & new providers
2025-03-09 07:14:19 +08:00
Fu Diwei
c7ad61e319 feat: add tencentcloud scf deployer 2025-03-08 14:58:40 +08:00
Fu Diwei
563a32ed62 fix: #495 2025-03-08 14:32:22 +08:00
Fu Diwei
1d4b88339e feat: add aliyun fc deployer 2025-03-08 14:30:01 +08:00
Fu Diwei
1e2e88e299 feat: allow insecure connections on deployment to some self-hosted services 2025-03-07 21:04:57 +08:00
Fu Diwei
29dda4ec66 feat: add 1panel deployer 2025-03-07 21:04:50 +08:00
Fu Diwei
6ccbdeb89a feat(ui): update default standard workflow template 2025-03-07 12:27:22 +08:00
Yoan.liu
5ae460c922 Merge pull request #488 from fudiwei/bugfix
serveral bugfix
2025-03-07 06:38:41 +08:00
Fu Diwei
48f3cc419b chore(ui): upgrade cron-parser 2025-03-07 00:18:39 +08:00
Fu Diwei
52e9341dab fix: inappropriate workflow node config unsaved reminder 2025-03-06 21:41:19 +08:00
Fu Diwei
411c39b148 fix: #485 2025-03-06 21:41:19 +08:00
Fu Diwei
574ad0445e refactor(ui): clean code 2025-03-06 21:41:16 +08:00
Fu Diwei
5b2bc6bff9 chore(ui): improve i18n 2025-03-06 18:28:43 +08:00
Fu Diwei
8a113e2bcb fix: missing parameter on deployment to tencentcloud ssl 2025-03-06 18:28:43 +08:00
Fu Diwei
9aaf3ff5d8 fix: #478 2025-03-06 18:28:43 +08:00
Fu Diwei
6d612f42a8 fix: #482 2025-03-06 18:28:43 +08:00
Fu Diwei
e2fdc29ca0 chore(deps): upgrade npm dependencies 2025-03-06 18:28:43 +08:00
Fu Diwei
b17dd04329 chore(deps): upgrade gomod dependencies 2025-03-06 18:28:31 +08:00
Fu Diwei
9a81d4a293 update README 2025-03-05 23:45:42 +08:00
Yoan.liu
5f971ea7e8 update version to v0.3.0 2025-03-05 21:17:58 +08:00
414 changed files with 18392 additions and 4339 deletions

77
.github/ISSUE_TEMPLATE/1-bug_report.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: "🐞 Bug Report"
description: "报告缺陷来帮助我们完善。 / Create a report to help us improve."
title: "[Bug] 简要描述你发现的缺陷"
labels:
- bug
body:
- type: markdown
attributes:
value: |
## Welcome!
**在提交 Issue 之前,请确认以下事项**
1. 我**确认**已尝试过使用当前最新版本,并能复现问题。
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
**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.
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.
- type: input
attributes:
label: 软件版本 / Release Version
description: 请提供 Certimate 的具体版本。 / Please provide the specific version of Certimate.
placeholder: (e.g. v1.0.0)
validations:
required: true
- type: textarea
attributes:
label: 缺陷描述 / Description
description: 请详细清晰地描述你发现的缺陷或故障,如果可能请上传截图。 / Describe the bug you found in detail and clearly, and upload screenshots if possible.
validations:
required: true
- type: textarea
attributes:
label: 复现步骤 / Steps to reproduce
description: 请提供可复现的完整步骤。 / Please walk us through it step by step.
placeholder: |
1. ...
2. ...
3. ...
...
validations:
required: true
- type: textarea
attributes:
label: 日志 / Logs
description: 在此处添加日志信息(如果有的话)。 / Add logs here if available.
value: |-
<details>
```console
# 请在此粘贴日志 / Paste logs here
```
</details>
validations:
required: false
- type: textarea
attributes:
label: 其他 / Miscellaneous
description: 在此处添加关于该 Issue 的任何其他信息。 / Add any other context about the issue here.
validations:
required: false
- type: markdown
attributes:
value: |
请保持每个 Issue 只包含一个缺陷报告。
Please limit one report per issue.

View File

@@ -0,0 +1,56 @@
name: "💡 Feature Request"
description: "提出新功能请求或改进意见。 / Suggest an idea for this project."
title: "[Feature] 简要描述你希望实现的功能"
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
## Welcome!
**在提交 Issue 之前,请确认以下事项**
1. 我**确认**是基于当前最新大版本而提出的新功能请求或改进意见。
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
**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.
- type: textarea
attributes:
label: 功能描述 / Description
description: 请详细清晰地描述你希望添加的功能,如果可能请上传截图。 / Describe the feature you'd like to add in detail and clearly, and upload screenshots if possible.
validations:
required: true
- type: textarea
attributes:
label: 请求动机 / Motivation
description: 为什么这个功能对项目有帮助? / Why is this feature helpful to the project?
validations:
required: true
- type: textarea
attributes:
label: 其他 / Miscellaneous
description: 在此处添加关于该 Issue 的任何其他信息。 / Add any other context about the problem here.
validations:
required: false
- type: checkboxes
attributes:
label: 贡献 / Contribution
options:
- label: 我乐意为此贡献代码! / I am interested in contributing to this feature!
required: false
- type: markdown
attributes:
value: |
请保持每个 Issue 只包含一个功能请求。
Please limit one request per issue.

48
.github/ISSUE_TEMPLATE/3-questions.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: "❓ Questions"
description: "遇到了困难需要求助? / Have problem in use and need help?"
title: "简要描述你遇到的问题"
body:
- type: markdown
attributes:
value: |
## Welcome!
**在提交 Issue 之前,请确认以下事项**
1. 我**确认**正在使用的是当前最新版本。
2. 我**确认**已搜索过[已有的 Issues](https://github.com/usual2970/certimate/issues)(包括已关闭的),没有类似的问题。
3. 我**确认**已阅读过[文档](https://docs.certimate.me/),没有类似的问题。
4. 请**务必**按照模板规范详细描述问题,否则 Issue 将会被直接关闭。
**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.
- type: input
attributes:
label: 软件版本 / Release Version
description: 请提供 Certimate 的具体版本。 / Please provide the specific version of Certimate.
placeholder: (e.g. v1.0.0)
validations:
required: true
- type: textarea
attributes:
label: 问题描述 / Description
description: 请详细清晰地描述你遇到的问题,如果可能请上传截图。 / Describe the problem you encountered in detail and clearly, and upload screenshots if possible.
validations:
required: true
- type: textarea
attributes:
label: 其他 / Miscellaneous
description: 在此处添加关于该问题的任何其他信息。 / Add any other context about the problem here.
validations:
required: false
- type: markdown
attributes:
value: |
请保持每个 Issue 只包含一个问题求助。
Please limit one question per issue.

View File

@@ -1,33 +0,0 @@
---
name: Bug report
about: 创建一个报告来帮助我们改进
title: "[Bug] 标题简要描述问题"
labels: bug
assignees: ""
---
**描述问题**
简要描述问题是什么1 个 ISSUE 只描述一个问题。
**复现步骤**
复现该问题的步骤:
1. 去到 '...'
2. 点击 '...'
3. 滚动到 '...'
4. 发现问题
**期望的结果**
简要描述你期望发生的事情。
**截图**
如有可能,请添加截图以帮助解释问题。
**环境**
- 操作系统: [e.g. Windows, macOS]
- 浏览器: [e.g. Chrome, Safari]
- 仓库版本: [e.g. v1.0.0]
**其他信息**
在此处添加关于该问题的任何其他信息。

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 加入频道讨论
url: https://t.me/+ZXphsppxUg41YmVl
about: 加入到电报频道寻求更多帮助
- name: "🌐 加入频道讨论"
about: "加入到电报频道寻求更多帮助。 / Join in our Telegram channel."
url: "https://t.me/+ZXphsppxUg41YmVl"
- name: "📖 常见问题"
about: "请先阅读文档 FAQ可能会有你需要的答案。 / Please take a look to FAQs."
url: "https://docs.certimate.me/docs/reference/faq"

View File

@@ -1,19 +0,0 @@
---
name: Feature request
about: 提出一个新功能请求
title: "[Feature] 简要描述你希望实现的功能"
labels: enhancement
assignees: ""
---
**功能描述**
简要描述你希望添加的功能和相关问题1 个 ISSUE 只描述一个功能。
**动机**
为什么这个功能对项目有帮助?
**替代方案**
描述你已经考虑过的替代方案。
**其他信息**
在这里添加任何相关的附加信息或截图。

View File

@@ -34,6 +34,11 @@ jobs:
images: |
usual2970/certimate
registry.cn-shanghai.aliyuncs.com/usual2970/certimate
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
- name: Log in to DOCKERHUB
uses: docker/login-action@v3
@@ -56,3 +61,4 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@@ -1,33 +1,23 @@
FROM node:20-alpine3.19 AS front-builder
FROM node:20-alpine3.19 AS webui-builder
WORKDIR /app
COPY . /app/
RUN \
cd /app/ui && \
npm install && \
npm run build
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY ../. /app/
RUN rm -rf /app/ui/dist
COPY --from=front-builder /app/ui/dist /app/ui/dist
COPY --from=webui-builder /app/ui/dist /app/ui/dist
RUN go build -o certimate
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/certimate .
ENTRYPOINT ["./certimate", "serve", "--http", "0.0.0.0:8090"]
ENTRYPOINT ["./certimate", "serve", "--http", "0.0.0.0:8090"]

View File

@@ -16,11 +16,6 @@
</div>
> [!WARNING]
> 当前分支为 `next`,是 v0.3.x 的开发分支,目前还没有稳定,请勿在生产环境中使用。
>
> 如需访问之前的版本,请切换至 `main` 分支。
---
## 🚩 项目简介
@@ -41,11 +36,12 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
## 💡 功能特性
- 灵活的工作流编排方式,证书从申请到部署完全自动化;
- 支持域名、多域名证书,可选 RSA、ECC 签名算法;
- 支持 20+ 域名托管商如阿里云、腾讯云、Cloudflare 等)
- 支持 50+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等
- 支持域名、多域名、泛域名证书,可选 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)
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
- 支持 Let's Encrypt、ZeroSSL、Google Trust Services 等多种 ACME 证书颁发机构;
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
- 更多特性等待探索。
## ⏱️ 快速启动
@@ -69,12 +65,13 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
如何使用 Docker 或其他部署方式请参考文档。
## 📄 技术文档
## 📄 使用手册
请访问 [docs.certimate.me](https://docs.certimate.me/) 以阅读技术文档
请访问文档站 [docs.certimate.me](https://docs.certimate.me/) 以阅读使用手册
相关文章:
- [使用 CNAME 完成 ACME DNS-01 质询](https://docs.certimate.me/blog/cname)
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)

View File

@@ -16,11 +16,6 @@
</div>
> [!WARNING]
> The current branch is `next`, which is the development branch for v0.3.x. It is currently unstable and should not be used in production environments.
>
> To access the previous versions, please switch to the `main` branch.
---
## 🚩 Introduction
@@ -40,12 +35,13 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate
## 💡 Features
- Flexible workflow orchestration, fully automated from certificate application to deployment;
- Supports wildcard, multi-domain certificates, with options for RSA or ECC.
- Supports more than 20+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc.);
- Supports more than 50+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc.);
- 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 multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
- Supports multiple certificate authorities including Let's Encrypt, ZeroSSL, Google Trust Services, and more;
- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust ServicesSSL.com, ZeroSSL, and more;
- More features waiting to be discovered.
## ⏱️ Fast Track
@@ -60,7 +56,7 @@ Download the archived package of precompiled binary files directly from [GitHub
Visit `http://127.0.0.1:8090` in your browser.
Initial administrator account:
Default administrator account:
- Username: `admin@certimate.fun`
- Password: `1234567890`
@@ -69,10 +65,11 @@ Work with Certimate right now. Or read other content in the documentation to lea
## 📄 Documentation
Please visit [docs.certimate.me](https://docs.certimate.me/en/).
Please visit the documentation site [docs.certimate.me](https://docs.certimate.me/en/).
Related articles:
- [使用 CNAME 完成 ACME DNS-01 质询](https://docs.certimate.me/blog/cname)
- [v0.3.0:第二个不向后兼容的大版本](https://docs.certimate.me/blog/v0.3.0)
- [v0.2.0:第一个不向后兼容的大版本](https://docs.certimate.me/blog/v0.2.0)
- [Why Certimate?](https://docs.certimate.me/blog/why-certimate)

View File

@@ -6,5 +6,7 @@ services:
ports:
- 8090:8090
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./data:/app/pb_data
restart: unless-stopped

134
go.mod
View File

@@ -6,71 +6,78 @@ toolchain go1.23.2
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
github.com/alibabacloud-go/alb-20200616/v2 v2.2.7
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/Edgio/edgio-api v0.0.0-workspace
github.com/G-Core/gcorelabscdn-go v1.0.28
github.com/alibabacloud-go/alb-20200616/v2 v2.2.8
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.0.10
github.com/alibabacloud-go/esa-20240910/v2 v2.13.0
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/fc-open-20210406/v2 v2.0.12
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.2.2
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.4
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/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go-v2/service/acm v1.30.18
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10
github.com/baidubce/bce-sdk-go v0.9.217
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.41
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/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.136
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141
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/pavlo-v-chernykh/keystore-go/v4 v4.5.0
github.com/pkg/sftp v1.13.7
github.com/pkg/sftp v1.13.8
github.com/pocketbase/dbx v1.11.0
github.com/pocketbase/pocketbase v0.25.4
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.1096
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1096
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1096
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096
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/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.195
github.com/volcengine/volcengine-go-sdk v1.0.180
golang.org/x/crypto v0.33.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
k8s.io/api v0.32.1
k8s.io/apimachinery v0.32.1
k8s.io/client-go v0.32.1
github.com/volcengine/volc-sdk-golang v1.0.199
github.com/volcengine/volcengine-go-sdk v1.0.187
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
software.sslmate.com/src/go-pkcs12 v0.5.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates v0.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 // 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.3.3 // indirect
github.com/G-Core/gcorelabscdn-go v1.0.26 // 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-utils v1.1.0 // indirect
github.com/alibabacloud-go/tea-rpc v1.1.3 // indirect
github.com/alibabacloud-go/tea-rpc-utils v1.1.0 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/alibabacloud-go/vod-20170321 v1.0.1 // indirect
github.com/alibabacloud-go/vod-20170321/v4 v4.6.1 // 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
@@ -84,6 +91,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-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
@@ -92,25 +100,25 @@ require (
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/jdcloud-api/jdcloud-sdk-go v1.62.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // 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/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/desec v0.10.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/peterhellberg/link v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/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/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099 // indirect
github.com/x448/float16 v0.8.4 // indirect
gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 // indirect
gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
@@ -136,21 +144,15 @@ require (
github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect
github.com/aliyun/credentials-go v1.4.3 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.1
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 // 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/feature/s3/manager v1.17.58 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // 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/internal/v4a v1.3.32 // 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/checksum v1.6.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.75.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
@@ -168,10 +170,8 @@ require (
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/google/uuid v1.6.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -187,37 +187,33 @@ require (
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/spf13/cobra v1.8.1 // 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/tjfoc/gmsm v1.4.1 // indirect
go.opencensus.io v0.24.0 // indirect
gocloud.dev v0.40.0 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.26.0 // indirect
golang.org/x/sync v0.11.0
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/image v0.25.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.30.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/api v0.220.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
google.golang.org/grpc v1.70.0 // indirect
golang.org/x/tools v0.31.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.11 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.34.5 // indirect
modernc.org/sqlite v1.36.1 // indirect
)
replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/vendors/edgio-sdk/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/ecloudsdkclouddns v1.0.1 => ./internal/pkg/vendors/cmcc-sdk/ecloudsdkclouddns@v1.0.1

380
go.sum
View File

@@ -1,5 +1,3 @@
cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0=
cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -15,26 +13,14 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -44,21 +30,16 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.47.0 h1:ajqgt30fnOMmLfWfu1PWcb+V9Dxz6n+9WKjdNg5R4HM=
cloud.google.com/go/storage v1.47.0/go.mod h1:Ks0vP374w0PW6jOUameJbapbQKXqkjGd/OJRp2fb9IQ=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
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.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
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/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
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=
@@ -75,14 +56,8 @@ 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.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -92,14 +67,8 @@ 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/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.26 h1:22SqETUw64s+It/op1T7y3ukEOU62CJOsUcsfSkhvZs=
github.com/G-Core/gcorelabscdn-go v1.0.26/go.mod h1:iSGXaTvZBzDHQW+rKFS918BgFVpONcyLEijwh8WsXpE=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI=
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/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=
@@ -117,8 +86,10 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
github.com/alibabacloud-go/alb-20200616/v2 v2.2.7 h1:IdNb5qq+/j2WdpCLOYphZD7ETkmxuPEZLs9qFmrXlEY=
github.com/alibabacloud-go/alb-20200616/v2 v2.2.7/go.mod h1:jU/K+GVb5b0vjiDpkf6E0dH77tsi1jTLGWm4ouCiRxk=
github.com/alibabacloud-go/alb-20200616/v2 v2.2.8 h1:/6+1AqIiENG3u6RmEYWEQ/YZv3YgdFZkE6Xd9RZM6n0=
github.com/alibabacloud-go/alb-20200616/v2 v2.2.8/go.mod h1:jU/K+GVb5b0vjiDpkf6E0dH77tsi1jTLGWm4ouCiRxk=
github.com/alibabacloud-go/alibabacloud-gateway-fc-util v0.0.7 h1:RDatRb9RG39HjkevgzTeiVoDDaamoB+12GHNairp3Ag=
github.com/alibabacloud-go/alibabacloud-gateway-fc-util v0.0.7/go.mod h1:H0RPHXHP/ICfEQrKzQcCqXI15jcV4zaDPCOAmh3U9O8=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
@@ -137,8 +108,9 @@ 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.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
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-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=
@@ -152,8 +124,12 @@ 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.13.0 h1:qLYLwczIIzoB1XxkyNC7rxbL3wkyBmVTAJZ+WZPtBTM=
github.com/alibabacloud-go/esa-20240910/v2 v2.13.0/go.mod h1:P1w/+i7dE2xSXVHJznEOVImlLtqqrzUJQQk2AsyBJ6o=
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/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/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=
@@ -176,19 +152,16 @@ github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy
github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
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 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
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-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-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-rpc v1.1.3 h1:uuxAIT9PB6MMABQfV/EMSnREZjh629WXu+hmPNF1IAs=
github.com/alibabacloud-go/tea-rpc v1.1.3/go.mod h1:uwhvnxPK69jcAYkVyP1WCFhTh1oVLiibUseSUpC7L8g=
github.com/alibabacloud-go/tea-rpc-utils v1.1.0 h1:kIG7+9sMRaDzvCbXfowycEwFRdnLAglRFQ/dnc0/JNE=
github.com/alibabacloud-go/tea-rpc-utils v1.1.0/go.mod h1:rxGY+fLbm3Fj3oJpeU0hBTmz52Ux50nm7JL01tyPv9c=
github.com/alibabacloud-go/tea-utils v1.3.0/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.3.6/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
@@ -204,17 +177,14 @@ 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 v1.0.1 h1:ZiBggVoJegu0Q3iarHZyveTOJJE0kUym6RCSLM9epoc=
github.com/alibabacloud-go/vod-20170321 v1.0.1/go.mod h1:eKaYMCAd22pgBFMz0Ci/o2l+UJSrq24LLSF/XyaTiac=
github.com/alibabacloud-go/vod-20170321/v4 v4.6.1 h1:6JTNq23lMo3wOui5qjpUJu2VKBgSHR4ArMgbKDOej7Q=
github.com/alibabacloud-go/vod-20170321/v4 v4.6.1/go.mod h1:TkgLKMSLu0qZN8Qdcu8svfHREyI64kjFvrp/GhrD4VQ=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.4 h1:Od0KgA73DyG9X2XFwuZZTkDv2pzA6B5mhYapyyca6QE=
github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.0.4/go.mod h1:DohGoS8BnMxHXghHebtjPP7+GMdxPsRN19T3nn2HcCU=
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/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.0/go.mod h1:ZXrrxv386Mj6z8NpihLKpexQE550m7j3LlyCvYub9aE=
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=
@@ -233,46 +203,32 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
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.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 h1:zAxi9p3wsZMIaVCdoiQp2uZ9k1LsZvmAnoTBeZPXom0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8/go.mod h1:3XkePX5dSaxveLAYY7nsbsZZrKxCyEuE5pM4ziFxyGg=
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/feature/s3/manager v1.17.58 h1:/BsEGAyMai+KdXS+CMHlLhB5miAO19wOqE6tj8azWPM=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.58/go.mod h1:KHM3lfl/sAJBCoLI1Lsg5w4SD2VDYWwQi7vxbKhw7TI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
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/internal/v4a v1.3.32 h1:OIHj/nAhVzIXGzbAE+4XmZ8FPvro3THr6NlqErJc3wY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32/go.mod h1:LiBEsDo34OJXqdDlRGsilhlIiXR7DL+6Cx2f4p1EgzI=
github.com/aws/aws-sdk-go-v2/service/acm v1.30.18 h1:/MZpjVk95P+lF9dUcOmyQwp1r0Ld4A8AxfQLdf1w8bU=
github.com/aws/aws-sdk-go-v2/service/acm v1.30.18/go.mod h1:JaIJpS5R/ADAyK2gGYcQSmpMyty24/nLxvwsPe629BI=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10 h1:fdLh7eMf5mxtggx2nG0+cFkaiRK+ULCOPK3qq8eTje4=
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10/go.mod h1:uBca+/1aH5v/RYWXqyymLrsbmx1vU9bBxeurlC627Gc=
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/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/checksum v1.6.0 h1:kT2WeWcFySdYpPgyqJMSUE7781Qucjtn6wBvrgm9P+M=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0/go.mod h1:WYH1ABybY7JK9TITPnk6ZlP7gQB8psI4c9qDmMsnLSA=
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/internal/s3shared v1.18.13 h1:OBsrtam3rk8NfBEq7OLOMm5HtQ9Yyw32X4UQMya/wjw=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13/go.mod h1:3U4gFA5pmoCOja7aq4nSaIAGbaOHv2Yl2ug018cmC+Q=
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/s3 v1.75.3 h1:JBod0SnNqcWQ0+uAyzeRFG1zCHotW8DukumYYyNy0zo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.75.3/go.mod h1:FHSHmyEUkzRbaFFqqm6bkLAOQHgqhsLmfCahvCBMiyA=
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=
@@ -282,10 +238,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98w
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.216 h1:jRq4C1UGYcvHo6Gst2kuUzhWwJM6EqXCmhIsTKQvf4k=
github.com/baidubce/bce-sdk-go v0.9.216/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.217 h1:dbMeVzpr9BGItTFHB1s2KSrpz0ayJC1y366VUMmaF0k=
github.com/baidubce/bce-sdk-go v0.9.217/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
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/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=
@@ -293,16 +247,14 @@ 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.41 h1:zLw2bwsW0gjNN1c9Zim1iv0g8ms+pV8pQ9yhLquOj1Q=
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.41/go.mod h1:7iCaE+dR9EycrJU0GQyMhptbInLbQhsKXiDKDjNi8Vs=
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/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=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -324,12 +276,10 @@ github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7F
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=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -358,18 +308,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
@@ -387,8 +331,6 @@ github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNan
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o=
github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI=
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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -409,8 +351,6 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -467,8 +407,6 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -532,23 +470,15 @@ 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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
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=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@@ -564,15 +494,20 @@ github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOj
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -587,10 +522,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.135 h1:UbNMlPfh0GhRY3iVkvv4fXFJ+bLqXoVCwjqe6geFdPs=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.135/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136 h1:T785NUg5245nWpPVHLVR8lBd+zGQYR14Vi/TCX1iu3A=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.136/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
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/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=
@@ -603,8 +536,8 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jdcloud-api/jdcloud-sdk-go v1.62.0 h1:uPfyOSY16mBrhggriDNeySFB4ZkzMMXpNac2P0fbDRw=
github.com/jdcloud-api/jdcloud-sdk-go v1.62.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
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/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -657,6 +590,11 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/libdns/dynv6 v1.0.0 h1:JpOK9TYRTHETAe+SIw3lk8SgUi3eD250GK+4fAHu4ys=
github.com/libdns/dynv6 v1.0.0/go.mod h1:65PL/bAlyH0J+0WGlOJYnMpoIuXcg/FmW4dTBYWtYUU=
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/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=
@@ -713,10 +651,14 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm
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/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=
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -742,6 +684,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ=
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ=
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
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/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -752,17 +696,15 @@ 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.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0=
github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U=
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.25.4 h1:3bsq+9RvLUmQs6bRlhuO0UiUnf9tt6aODPTHQlj8pYk=
github.com/pocketbase/pocketbase v0.25.4/go.mod h1:CfcfWJ2u4eWaQbrpZ1rEkqIk9rB521yb9JVLNpEl/8E=
github.com/pocketbase/pocketbase v0.26.1 h1:0WBqIRKKPCqp+xHPVLB4fevkoT9HVlR4BSuNwAt5oJ0=
github.com/pocketbase/pocketbase v0.26.1/go.mod h1:t5y5pfnhrEg//RuSzSg0a926OLZ0oQj66jYs3BzDJwA=
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=
@@ -825,9 +767,8 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
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/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@@ -849,35 +790,37 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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.1096 h1:/nbiqUXO5/7WYATAGt65ULSEB48q+gCws6rI8kkt8Ck=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1096/go.mod h1:EtQl1Sj7WaofRoyetOsDqCO+MvnFMba3ZUrD9KZo5sQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1096 h1:Z9I0iPNWfwbwRcPrWaY5w5WL2d6NrlyBI1dSGq7jobk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1096/go.mod h1:lQBepDW2LTraTn+zOwgtoU1zVmKvgsUWqMjtmTRjn30=
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.1096 h1:DMokC7T0UF8wMfT1kD+mX3M+hc2C06gmFvQ9gsfRPmI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1099 h1:4fQ53ORk6Eayw1H2kg43PoBnUuhGR6WRG6rtec/i3oI=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1099/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102 h1:DxsNhw67OHyQME20IULmi8lgNY9MHuQ+qS1XH1/yTvM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1102/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/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.1096 h1:h9FP40Ycg45egJlZcjbLyc4IUeFoq+wSpR43sHMALtM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1096/go.mod h1:+Np3rsf08RDXsl8+7/MC4QcUeOuZj0KwNhA8tkL7BXg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096 h1:7ZmPusU52i5bT9bf5xBeqJclyBx75zFGEIOmiXme9YU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1096/go.mod h1:aMpGcDskqqhXtfMaeo2egO61tgh/zt07L1ohSPwmjWk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096 h1:N62IFKL1ZRNQ7WPLNn8x9eYnwM4lOUIVY3buW6kbGtg=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1096/go.mod h1:4PZRRpZp+jvYBUbUajsoZREnk7sJXMnPAiGB4IX8IkM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102 h1:B0mJk0ojVOFCMLrBoxLNVgrGih11EezTekRffkACCAY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1102/go.mod h1:0yyQ1r35jteb5DV4mcJZ5uh9NStWzjMYz9iSMnDMdJA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099 h1:kD+8RKF0uJCr7VaurAUA11NNAoln0HaagMCgQV6EnUw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1099/go.mod h1:ellbjD8eHKHS4ixscLdiPJI8QoFIk0YNEgaDjxXMECM=
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/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/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=
@@ -892,12 +835,10 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X
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/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/volcengine/volc-sdk-golang v1.0.194 h1:3o0INQzdtYJWvdGrtX02booCqPL5TsWSq2W1Ur7Bzlo=
github.com/volcengine/volc-sdk-golang v1.0.194/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE=
github.com/volcengine/volc-sdk-golang v1.0.195 h1:hKX4pBhmKcB3652BTdcAmtgizEPBnoQUpTM+j5blMA4=
github.com/volcengine/volc-sdk-golang v1.0.195/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/volcengine/volcengine-go-sdk v1.0.180 h1:lzcNlaxeGIUdXgDuVH7KJwZYZjIZzaCAYPDh91htU6U=
github.com/volcengine/volcengine-go-sdk v1.0.180/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA=
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/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=
@@ -926,26 +867,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8=
go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
@@ -955,8 +876,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
gocloud.dev v0.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng=
gocloud.dev v0.40.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -979,13 +898,16 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
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/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=
@@ -999,14 +921,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-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
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/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.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
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=
@@ -1029,8 +951,11 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1079,20 +1004,23 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
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.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
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/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.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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/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=
@@ -1106,8 +1034,12 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/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=
@@ -1176,26 +1108,33 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.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/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=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
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/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=
@@ -1211,8 +1150,11 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
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/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=
@@ -1272,14 +1214,14 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
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/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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
@@ -1300,8 +1242,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns=
google.golang.org/api v0.220.0/go.mod h1:26ZAlY6aN/8WgpCzjPNy18QpYaz7Zgg1h0qe1GkZEmY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1340,12 +1280,6 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1363,8 +1297,6 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1425,12 +1357,12 @@ 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.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
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/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=
@@ -1439,15 +1371,15 @@ k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJ
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.15 h1:wFDan71KnYqeHz4eF63vmGE6Q6Pc0PUGDpP0PRMYjDc=
modernc.org/ccgo/v4 v4.23.15/go.mod h1:nJX30dks/IWuBOnVa7VRii9Me4/9TZ1SC9GNtmARTy0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
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.2 h1:YBXi5Kqp6aCK3fIxwKQ3/fErvawVKwjOLItxj1brGds=
modernc.org/gc/v2 v2.6.2/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.11 h1:6sZG8uB6EMMG7iTLPTndi8jyTdgAQNIeLGjCFICACZw=
modernc.org/libc v1.61.11/go.mod h1:HHX+srFdn839oaJRd0W8hBM3eg+mieyZCAjWwB08/nM=
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/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=
@@ -1456,8 +1388,8 @@ 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.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ=
modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
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=

View File

@@ -3,6 +3,7 @@ package app
import (
"sync"
"time"
_ "time/tzdata"
"github.com/pocketbase/pocketbase/tools/cron"
)

View File

@@ -1,38 +1,30 @@
package applicant
const (
sslProviderLetsEncrypt = "letsencrypt"
sslProviderLetsEncryptStaging = "letsencrypt_staging"
sslProviderZeroSSL = "zerossl"
sslProviderGoogleTrustServices = "gts"
)
const defaultSSLProvider = sslProviderLetsEncrypt
import "github.com/usual2970/certimate/internal/domain"
const (
letsencryptUrl = "https://acme-v02.api.letsencrypt.org/directory"
letsencryptStagingUrl = "https://acme-staging-v02.api.letsencrypt.org/directory"
zerosslUrl = "https://acme.zerossl.com/v2/DV90"
gtsUrl = "https://dv.acme-v02.api.pki.goog/directory"
sslProviderLetsEncrypt = string(domain.ApplyCAProviderTypeLetsEncrypt)
sslProviderLetsEncryptStaging = string(domain.ApplyCAProviderTypeLetsEncryptStaging)
sslProviderBuypass = string(domain.ApplyCAProviderTypeBuypass)
sslProviderGoogleTrustServices = string(domain.ApplyCAProviderTypeGoogleTrustServices)
sslProviderSSLCom = string(domain.ApplyCAProviderTypeSSLCom)
sslProviderZeroSSL = string(domain.ApplyCAProviderTypeZeroSSL)
sslProviderDefault = sslProviderLetsEncrypt
)
var sslProviderUrls = map[string]string{
sslProviderLetsEncrypt: letsencryptUrl,
sslProviderLetsEncryptStaging: letsencryptStagingUrl,
sslProviderZeroSSL: zerosslUrl,
sslProviderGoogleTrustServices: gtsUrl,
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",
}
type acmeSSLProviderConfig struct {
Config acmeSSLProviderConfigContent `json:"config"`
Provider string `json:"provider"`
}
type acmeSSLProviderConfigContent struct {
ZeroSSL acmeSSLProviderEabConfig `json:"zerossl"`
GoogleTrustServices acmeSSLProviderEabConfig `json:"gts"`
}
type acmeSSLProviderEabConfig struct {
EabHmacKey string `json:"eabHmacKey"`
EabKid string `json:"eabKid"`
Config map[domain.ApplyCAProviderType]map[string]any `json:"config"`
Provider string `json:"provider"`
}

View File

@@ -13,7 +13,8 @@ import (
"golang.org/x/sync/singleflight"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
"github.com/usual2970/certimate/internal/repository"
)
@@ -40,7 +41,7 @@ func newAcmeUser(ca, email string) (*acmeUser, error) {
return nil, err
}
keyPEM, err := certs.ConvertECPrivateKeyToPEM(key)
keyPEM, err := certutil.ConvertECPrivateKeyToPEM(key)
if err != nil {
return nil, err
}
@@ -64,7 +65,7 @@ func (u acmeUser) GetRegistration() *registration.Resource {
}
func (u *acmeUser) GetPrivateKey() crypto.PrivateKey {
rs, _ := certs.ParseECPrivateKeyFromPEM(u.privkey)
rs, _ := certutil.ParseECPrivateKeyFromPEM(u.privkey)
return rs
}
@@ -76,16 +77,11 @@ func (u *acmeUser) getPrivateKeyPEM() string {
return u.privkey
}
type acmeAccountRepository interface {
GetByCAAndEmail(ca, email string) (*domain.AcmeAccount, error)
Save(ca, email, key string, resource *registration.Resource) error
}
var registerGroup singleflight.Group
func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", sslProviderConfig.Provider, user.GetEmail()), func() (interface{}, error) {
return registerAcmeUser(client, sslProviderConfig, user)
func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
resp, err, _ := registerGroup.Do(fmt.Sprintf("register_acme_user_%s_%s", user.CA, user.Email), func() (interface{}, error) {
return registerAcmeUser(client, user, userRegisterOptions)
})
if err != nil {
@@ -95,45 +91,81 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, sslProviderConfig *ac
return resp.(*registration.Resource), nil
}
func registerAcmeUser(client *lego.Client, sslProviderConfig *acmeSSLProviderConfig, user *acmeUser) (*registration.Resource, error) {
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
var reg *registration.Resource
var err error
switch sslProviderConfig.Provider {
case sslProviderZeroSSL:
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: sslProviderConfig.Config.ZeroSSL.EabKid,
HmacEncoded: sslProviderConfig.Config.ZeroSSL.EabHmacKey,
})
case sslProviderGoogleTrustServices:
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: sslProviderConfig.Config.GoogleTrustServices.EabKid,
HmacEncoded: sslProviderConfig.Config.GoogleTrustServices.EabHmacKey,
})
switch user.CA {
case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
case sslProviderBuypass:
{
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
case sslProviderGoogleTrustServices:
{
access := domain.AccessConfigForGoogleTrustServices{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: access.EabKid,
HmacEncoded: access.EabHmacKey,
})
}
case sslProviderSSLCom:
{
access := domain.AccessConfigForSSLCom{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: access.EabKid,
HmacEncoded: access.EabHmacKey,
})
}
case sslProviderZeroSSL:
{
access := domain.AccessConfigForZeroSSL{}
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: true,
Kid: access.EabKid,
HmacEncoded: access.EabHmacKey,
})
}
default:
err = fmt.Errorf("unsupported ssl provider: %s", sslProviderConfig.Provider)
err = fmt.Errorf("unsupported ca provider: %s", user.CA)
}
if err != nil {
return nil, err
}
repo := repository.NewAcmeAccountRepository()
resp, err := repo.GetByCAAndEmail(sslProviderConfig.Provider, user.GetEmail())
resp, err := repo.GetByCAAndEmail(user.CA, user.Email)
if err == nil {
user.privkey = resp.Key
return resp.Resource, nil
}
if _, err := repo.Save(context.Background(), &domain.AcmeAccount{
CA: sslProviderConfig.Provider,
Email: user.GetEmail(),
CA: user.CA,
Email: user.Email,
Key: user.getPrivateKeyPEM(),
Resource: reg,
}); err != nil {
return nil, fmt.Errorf("failed to save registration: %w", err)
return nil, fmt.Errorf("failed to save acme account registration: %w", err)
}
return reg, nil

View File

@@ -18,7 +18,7 @@ import (
"golang.org/x/time/rate"
"github.com/usual2970/certimate/internal/domain"
uslices "github.com/usual2970/certimate/internal/pkg/utils/slices"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
"github.com/usual2970/certimate/internal/repository"
)
@@ -37,18 +37,21 @@ type Applicant interface {
}
type applicantOptions struct {
Domains []string
ContactEmail string
Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any
ProviderApplyConfig map[string]any
KeyAlgorithm string
Nameservers []string
DnsPropagationTimeout int32
DnsTTL int32
DisableFollowCNAME bool
ReplacedARIAcctId string
ReplacedARICertId string
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
}
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
@@ -58,27 +61,55 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
nodeConfig := node.GetConfigForApply()
options := &applicantOptions{
Domains: uslices.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
ContactEmail: nodeConfig.ContactEmail,
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
ProviderApplyConfig: nodeConfig.ProviderConfig,
KeyAlgorithm: nodeConfig.KeyAlgorithm,
Nameservers: uslices.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
DnsTTL: nodeConfig.DnsTTL,
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
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,
}
accessRepo := repository.NewAccessRepository()
if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil {
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
} else {
accessConfig, err := access.UnmarshalConfigToMap()
if err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
if nodeConfig.ProviderAccessId != "" {
if access, err := accessRepo.GetById(context.Background(), nodeConfig.ProviderAccessId); err != nil {
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.ProviderAccessId, err)
} else {
options.ProviderAccessConfig = access.Config
}
}
if nodeConfig.CAProviderAccessId != "" {
if access, err := accessRepo.GetById(context.Background(), nodeConfig.CAProviderAccessId); err != nil {
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.CAProviderAccessId, err)
} else {
options.CAProviderAccessConfig = access.Config
}
}
settingsRepo := repository.NewSettingsRepository()
if string(options.CAProvider) == "" {
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
sslProviderConfig := &acmeSSLProviderConfig{
Config: make(map[domain.ApplyCAProviderType]map[string]any),
Provider: sslProviderDefault,
}
if settings != nil {
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
return nil, err
} else if sslProviderConfig.Provider == "" {
sslProviderConfig.Provider = sslProviderDefault
}
}
options.ProviderAccessConfig = accessConfig
options.CAProvider = domain.ApplyCAProviderType(sslProviderConfig.Provider)
options.CAProviderAccessConfig = sslProviderConfig.Config[options.CAProvider]
}
certRepo := repository.NewCertificateRepository()
@@ -93,8 +124,8 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate))
if lastCertX509 != nil {
replacedARICertId, _ := certificate.MakeARICertID(lastCertX509)
options.ReplacedARIAcctId = lastCertificate.ACMEAccountUrl
options.ReplacedARICertId = replacedARICertId
options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl
options.ReplacedARICert = replacedARICertId
}
}
}
@@ -111,24 +142,7 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
}
func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) {
settingsRepo := repository.NewSettingsRepository()
settings, _ := settingsRepo.GetByName(context.Background(), "sslProvider")
sslProviderConfig := &acmeSSLProviderConfig{
Config: acmeSSLProviderConfigContent{},
Provider: defaultSSLProvider,
}
if settings != nil {
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
return nil, err
}
}
if sslProviderConfig.Provider == "" {
sslProviderConfig.Provider = defaultSSLProvider
}
acmeUser, err := newAcmeUser(sslProviderConfig.Provider, options.ContactEmail)
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
if err != nil {
return nil, err
}
@@ -138,9 +152,16 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", strconv.FormatBool(options.DisableFollowCNAME))
// Create an ACME client config
config := lego.NewConfig(acmeUser)
config.CADirURL = sslProviderUrls[sslProviderConfig.Provider]
config := lego.NewConfig(user)
config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
config.CADirURL = sslProviderUrls[user.CA]
if user.CA == sslProviderSSLCom {
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"]
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"]
}
}
// Create an ACME client
client, err := lego.NewClient(config)
@@ -157,12 +178,12 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...)
// New users need to register first
if !acmeUser.hasRegistration() {
reg, err := registerAcmeUserWithSingleFlight(client, sslProviderConfig, acmeUser)
if !user.hasRegistration() {
reg, err := registerAcmeUserWithSingleFlight(client, user, options.CAProviderAccessConfig)
if err != nil {
return nil, fmt.Errorf("failed to register: %w", err)
}
acmeUser.Registration = reg
user.Registration = reg
}
// Obtain a certificate
@@ -170,8 +191,8 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
Domains: options.Domains,
Bundle: true,
}
if options.ReplacedARICertId != "" && options.ReplacedARIAcctId != acmeUser.Registration.URI {
certRequest.ReplacesCertID = options.ReplacedARICertId
if options.ReplacedARIAcct == user.Registration.URI {
certRequest.ReplacesCertID = options.ReplacedARICert
}
certResource, err := client.Certificate.Obtain(certRequest)
if err != nil {
@@ -182,7 +203,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
ACMEAccountUrl: acmeUser.Registration.URI,
ACMEAccountUrl: user.Registration.URI,
ACMECertUrl: certResource.CertURL,
ACMECertStableUrl: certResource.CertStableURL,
CSR: strings.TrimSpace(string(certResource.CSR)),
@@ -203,6 +224,8 @@ func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyTy
return certcrypto.EC256
case domain.CertificateKeyAlgorithmTypeEC384:
return certcrypto.EC384
case domain.CertificateKeyAlgorithmTypeEC512:
return certcrypto.KeyType("P512")
}
return certcrypto.RSA2048

View File

@@ -14,7 +14,9 @@ import (
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"
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec"
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla"
pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6"
pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore"
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
@@ -24,12 +26,15 @@ import (
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"
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"
pRainYun "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/rainyun"
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud"
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo"
pVercel "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/vercel"
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/volcengine"
pWestcn "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/westcn"
"github.com/usual2970/certimate/internal/pkg/utils/maps"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
)
func createApplicant(options *applicantOptions) (challenge.Provider, error) {
@@ -41,7 +46,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeACMEHttpReq:
{
access := domain.AccessConfigForACMEHttpReq{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -58,7 +63,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeAliyun, domain.ApplyDNSProviderTypeAliyunDNS:
{
access := domain.AccessConfigForAliyun{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -74,15 +79,15 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeAWS, domain.ApplyDNSProviderTypeAWSRoute53:
{
access := domain.AccessConfigForAWS{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
HostedZoneId: maps.GetValueAsString(options.ProviderApplyConfig, "hostedZoneId"),
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -92,7 +97,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
{
access := domain.AccessConfigForAzure{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -110,7 +115,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeBaiduCloud, domain.ApplyDNSProviderTypeBaiduCloudDNS:
{
access := domain.AccessConfigForBaiduCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -126,12 +131,13 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeCloudflare:
{
access := domain.AccessConfigForCloudflare{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pCloudflare.NewChallengeProvider(&pCloudflare.ChallengeProviderConfig{
DnsApiToken: access.DnsApiToken,
ZoneApiToken: access.ZoneApiToken,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -141,7 +147,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeClouDNS:
{
access := domain.AccessConfigForClouDNS{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -157,7 +163,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeCMCCCloud:
{
access := domain.AccessConfigForCMCCCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -170,10 +176,25 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeDeSEC:
{
access := domain.AccessConfigForDeSEC{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pDeSEC.NewChallengeProvider(&pDeSEC.ChallengeProviderConfig{
Token: access.Token,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeDNSLA:
{
access := domain.AccessConfigForDNSLA{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -186,10 +207,25 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeDynv6:
{
access := domain.AccessConfigForDynv6{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pDynv6.NewChallengeProvider(&pDynv6.ChallengeProviderConfig{
HttpToken: access.HttpToken,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypeGcore:
{
access := domain.AccessConfigForGcore{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -204,7 +240,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeGname:
{
access := domain.AccessConfigForGname{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -220,7 +256,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeGoDaddy:
{
access := domain.AccessConfigForGoDaddy{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -236,14 +272,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeHuaweiCloud, domain.ApplyDNSProviderTypeHuaweiCloudDNS:
{
access := domain.AccessConfigForHuaweiCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maps.GetValueAsString(options.ProviderApplyConfig, "region"),
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -253,14 +289,14 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
{
access := domain.AccessConfigForJDCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
RegionId: maps.GetValueAsString(options.ProviderApplyConfig, "region_id"),
RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -270,7 +306,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeNamecheap:
{
access := domain.AccessConfigForNamecheap{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -286,7 +322,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeNameDotCom:
{
access := domain.AccessConfigForNameDotCom{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -302,7 +338,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeNameSilo:
{
access := domain.AccessConfigForNameSilo{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -317,7 +353,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeNS1:
{
access := domain.AccessConfigForNS1{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -329,10 +365,26 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypePorkbun:
{
access := domain.AccessConfigForPorkbun{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pPorkbun.NewChallengeProvider(&pPorkbun.ChallengeProviderConfig{
ApiKey: access.ApiKey,
SecretApiKey: access.SecretApiKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
}
case domain.ApplyDNSProviderTypePowerDNS:
{
access := domain.AccessConfigForPowerDNS{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -348,7 +400,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeRainYun:
{
access := domain.AccessConfigForRainYun{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -360,16 +412,48 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS, domain.ApplyDNSProviderTypeTencentCloudEO:
{
access := domain.AccessConfigForTencentCloud{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
switch options.Provider {
case domain.ApplyDNSProviderTypeTencentCloud, domain.ApplyDNSProviderTypeTencentCloudDNS:
applicant, err := pTencentCloud.NewChallengeProvider(&pTencentCloud.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
case domain.ApplyDNSProviderTypeTencentCloudEO:
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
SecretId: access.SecretId,
SecretKey: access.SecretKey,
ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
return applicant, err
default:
break
}
}
case domain.ApplyDNSProviderTypeVercel:
{
access := domain.AccessConfigForVercel{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
applicant, err := pVercel.NewChallengeProvider(&pVercel.ChallengeProviderConfig{
ApiAccessToken: access.ApiAccessToken,
TeamId: access.TeamId,
DnsPropagationTimeout: options.DnsPropagationTimeout,
DnsTTL: options.DnsTTL,
})
@@ -379,7 +463,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeVolcEngine, domain.ApplyDNSProviderTypeVolcEngineDNS:
{
access := domain.AccessConfigForVolcEngine{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
@@ -395,7 +479,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
case domain.ApplyDNSProviderTypeWestcn:
{
access := domain.AccessConfigForWestcn{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}

View File

@@ -11,11 +11,13 @@ import (
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/pocketbase/dbx"
"github.com/usual2970/certimate/internal/app"
"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/certs"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
"github.com/usual2970/certimate/internal/repository"
)
@@ -27,21 +29,29 @@ const (
type certificateRepository interface {
ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error)
GetById(ctx context.Context, id string) (*domain.Certificate, error)
DeleteWhere(ctx context.Context, exprs ...dbx.Expression) (int, error)
}
type settingsRepository interface {
GetByName(ctx context.Context, name string) (*domain.Settings, error)
}
type CertificateService struct {
certRepo certificateRepository
certificateRepo certificateRepository
settingsRepo settingsRepository
}
func NewCertificateService(certRepo certificateRepository) *CertificateService {
func NewCertificateService(certificateRepo certificateRepository, settingsRepo settingsRepository) *CertificateService {
return &CertificateService{
certRepo: certRepo,
certificateRepo: certificateRepo,
settingsRepo: settingsRepo,
}
}
func (s *CertificateService) InitSchedule(ctx context.Context) error {
// 每日发送过期证书提醒
app.GetScheduler().MustAdd("certificateExpireSoonNotify", "0 0 * * *", func() {
certificates, err := s.certRepo.ListExpireSoon(context.Background())
certificates, err := s.certificateRepo.ListExpireSoon(context.Background())
if err != nil {
app.GetLogger().Error("failed to get certificates which expire soon", "err", err)
return
@@ -56,11 +66,37 @@ func (s *CertificateService) InitSchedule(ctx context.Context) error {
app.GetLogger().Error("failed to send notification", "err", err)
}
})
// 每日清理过期证书
app.GetScheduler().MustAdd("certificateExpiredCleanup", "0 0 * * *", func() {
settings, err := s.settingsRepo.GetByName(ctx, "persistence")
if err != nil {
app.GetLogger().Error("failed to get persistence settings", "err", err)
return
}
var settingsContent *domain.PersistenceSettingsContent
json.Unmarshal([]byte(settings.Content), &settingsContent)
if settingsContent != nil && settingsContent.ExpiredCertificatesMaxDaysRetention != 0 {
ret, err := s.certificateRepo.DeleteWhere(
context.Background(),
dbx.NewExp(fmt.Sprintf("expireAt<DATETIME('now', '-%d days')", settingsContent.ExpiredCertificatesMaxDaysRetention)),
)
if err != nil {
app.GetLogger().Error("failed to delete expired certificates", "err", err)
}
if ret > 0 {
app.GetLogger().Info(fmt.Sprintf("cleanup %d expired certificates", ret))
}
}
})
return nil
}
func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.CertificateArchiveFileReq) (*dtos.CertificateArchiveFileResp, error) {
certificate, err := s.certRepo.GetById(ctx, req.CertificateId)
certificate, err := s.certificateRepo.GetById(ctx, req.CertificateId)
if err != nil {
return nil, err
}
@@ -109,7 +145,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
{
const pfxPassword = "certimate"
certPFX, err := certs.TransformCertificateFromPEMToPFX(certificate.Certificate, certificate.PrivateKey, pfxPassword)
certPFX, err := certutil.TransformCertificateFromPEMToPFX(certificate.Certificate, certificate.PrivateKey, pfxPassword)
if err != nil {
return nil, err
}
@@ -147,7 +183,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
{
const jksPassword = "certimate"
certJKS, err := certs.TransformCertificateFromPEMToJKS(certificate.Certificate, certificate.PrivateKey, jksPassword, jksPassword, jksPassword)
certJKS, err := certutil.TransformCertificateFromPEMToJKS(certificate.Certificate, certificate.PrivateKey, jksPassword, jksPassword, jksPassword)
if err != nil {
return nil, err
}
@@ -187,7 +223,7 @@ func (s *CertificateService) ArchiveFile(ctx context.Context, req *dtos.Certific
}
func (s *CertificateService) ValidateCertificate(ctx context.Context, req *dtos.CertificateValidateCertificateReq) (*dtos.CertificateValidateCertificateResp, error) {
certX509, err := certs.ParseCertificateFromPEM(req.Certificate)
certX509, err := certutil.ParseCertificateFromPEM(req.Certificate)
if err != nil {
return nil, err
} else if time.Now().After(certX509.NotAfter) {

View File

@@ -3,14 +3,16 @@ package deployer
import (
"context"
"fmt"
"log/slog"
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/repository"
)
type Deployer interface {
SetLogger(*slog.Logger)
Deploy(ctx context.Context) error
}
@@ -30,29 +32,28 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
}
nodeConfig := node.GetConfigForDeploy()
options := &deployerOptions{
Provider: domain.DeployProviderType(nodeConfig.Provider),
ProviderAccessConfig: make(map[string]any),
ProviderDeployConfig: nodeConfig.ProviderConfig,
}
accessRepo := repository.NewAccessRepository()
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)
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
}
}
accessConfig, err := access.UnmarshalConfigToMap()
if err != nil {
return nil, fmt.Errorf("failed to unmarshal access config: %w", err)
}
deployer, err := createDeployer(&deployerOptions{
Provider: domain.DeployProviderType(nodeConfig.Provider),
ProviderAccessConfig: accessConfig,
ProviderDeployConfig: nodeConfig.ProviderConfig,
})
deployer, err := createDeployer(options)
if err != nil {
return nil, err
}
return &proxyDeployer{
logger: logger.NewNilLogger(),
deployer: deployer,
deployCertificate: certdata.Certificate,
deployPrivateKey: certdata.PrivateKey,
@@ -61,12 +62,19 @@ func NewWithDeployNode(node *domain.WorkflowNode, certdata struct {
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
type proxyDeployer struct {
logger logger.Logger
deployer deployer.Deployer
deployCertificate string
deployPrivateKey string
}
func (d *proxyDeployer) SetLogger(logger *slog.Logger) {
if logger == nil {
panic("logger is nil")
}
d.deployer.WithLogger(logger)
}
func (d *proxyDeployer) Deploy(ctx context.Context) error {
_, err := d.deployer.Deploy(ctx, d.deployCertificate, d.deployPrivateKey)
return err

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
package domain
import (
"encoding/json"
"time"
)
@@ -9,19 +8,16 @@ const CollectionNameAccess = "access"
type Access struct {
Meta
Name string `json:"name" db:"name"`
Provider string `json:"provider" db:"provider"`
Config string `json:"config" db:"config"`
DeletedAt *time.Time `json:"deleted" db:"deleted"`
Name string `json:"name" db:"name"`
Provider string `json:"provider" db:"provider"`
Config map[string]any `json:"config" db:"config"`
DeletedAt *time.Time `json:"deleted" db:"deleted"`
}
func (a *Access) UnmarshalConfigToMap() (map[string]any, error) {
config := make(map[string]any)
if err := json.Unmarshal([]byte(a.Config), &config); err != nil {
return nil, err
}
return config, nil
type AccessConfigFor1Panel struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForACMEHttpReq struct {
@@ -58,8 +54,9 @@ type AccessConfigForBaishan struct {
}
type AccessConfigForBaotaPanel struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForBytePlus struct {
@@ -78,7 +75,8 @@ type AccessConfigForCdnfly struct {
}
type AccessConfigForCloudflare struct {
DnsApiToken string `json:"dnsApiToken"`
DnsApiToken string `json:"dnsApiToken"`
ZoneApiToken string `json:"zoneApiToken,omitempty"`
}
type AccessConfigForClouDNS struct {
@@ -91,6 +89,10 @@ type AccessConfigForCMCCCloud struct {
AccessKeySecret string `json:"accessKeySecret"`
}
type AccessConfigForDeSEC struct {
Token string `json:"token"`
}
type AccessConfigForDNSLA struct {
ApiId string `json:"apiId"`
ApiSecret string `json:"apiSecret"`
@@ -101,6 +103,10 @@ type AccessConfigForDogeCloud struct {
SecretKey string `json:"secretKey"`
}
type AccessConfigForDynv6 struct {
HttpToken string `json:"httpToken"`
}
type AccessConfigForEdgio struct {
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
@@ -120,6 +126,11 @@ type AccessConfigForGoDaddy struct {
ApiSecret string `json:"apiSecret"`
}
type AccessConfigForGoogleTrustServices struct {
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}
type AccessConfigForHuaweiCloud struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
@@ -134,8 +145,6 @@ type AccessConfigForKubernetes struct {
KubeConfig string `json:"kubeConfig,omitempty"`
}
type AccessConfigForLocal struct{}
type AccessConfigForNamecheap struct {
Username string `json:"username"`
ApiKey string `json:"apiKey"`
@@ -154,6 +163,11 @@ type AccessConfigForNS1 struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForPorkbun struct {
ApiKey string `json:"apiKey"`
SecretApiKey string `json:"secretApiKey"`
}
type AccessConfigForPowerDNS struct {
ApiUrl string `json:"apiUrl"`
ApiKey string `json:"apiKey"`
@@ -169,8 +183,9 @@ type AccessConfigForRainYun struct {
}
type AccessConfigForSafeLine struct {
ApiUrl string `json:"apiUrl"`
ApiToken string `json:"apiToken"`
ApiUrl string `json:"apiUrl"`
ApiToken string `json:"apiToken"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForSSH struct {
@@ -182,6 +197,11 @@ type AccessConfigForSSH struct {
KeyPassphrase string `json:"keyPassphrase,omitempty"`
}
type AccessConfigForSSLCom struct {
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}
type AccessConfigForTencentCloud struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
@@ -193,16 +213,37 @@ type AccessConfigForUCloud struct {
ProjectId string `json:"projectId,omitempty"`
}
type AccessConfigForUpyun struct {
Username string `json:"username"`
Password string `json:"password"`
}
type AccessConfigForVercel struct {
ApiAccessToken string `json:"apiAccessToken"`
TeamId string `json:"teamId,omitempty"`
}
type AccessConfigForVolcEngine struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
}
type AccessConfigForWangsu struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
}
type AccessConfigForWebhook struct {
Url string `json:"url"`
Url string `json:"url"`
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForWestcn struct {
Username string `json:"username"`
ApiPassword string `json:"password"`
}
type AccessConfigForZeroSSL struct {
EabKid string `json:"eabKid"`
EabHmacKey string `json:"eabHmacKey"`
}

View File

@@ -1,11 +1,14 @@
package domain
import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"fmt"
"strings"
"time"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
)
const CollectionNameCertificate = "certificate"
@@ -39,19 +42,58 @@ func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate
c.EffectAt = certX509.NotBefore
c.ExpireAt = certX509.NotAfter
switch certX509.SignatureAlgorithm {
case x509.SHA256WithRSA, x509.SHA256WithRSAPSS:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA2048
case x509.SHA384WithRSA, x509.SHA384WithRSAPSS:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA3072
case x509.SHA512WithRSA, x509.SHA512WithRSAPSS:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA4096
case x509.ECDSAWithSHA256:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC256
case x509.ECDSAWithSHA384:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC384
case x509.ECDSAWithSHA512:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC512
switch certX509.PublicKeyAlgorithm {
case x509.RSA:
{
len := 0
if pubkey, ok := certX509.PublicKey.(*rsa.PublicKey); ok {
len = pubkey.N.BitLen()
}
switch len {
case 0:
c.KeyAlgorithm = CertificateKeyAlgorithmType("RSA")
case 2048:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA2048
case 3072:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA3072
case 4096:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA4096
case 8192:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeRSA8192
default:
c.KeyAlgorithm = CertificateKeyAlgorithmType(fmt.Sprintf("RSA%d", len))
}
}
case x509.ECDSA:
{
len := 0
if pubkey, ok := certX509.PublicKey.(*ecdsa.PublicKey); ok {
if pubkey.Curve != nil && pubkey.Curve.Params() != nil {
len = pubkey.Curve.Params().BitSize
}
}
switch len {
case 0:
c.KeyAlgorithm = CertificateKeyAlgorithmType("EC")
case 256:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC256
case 384:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC384
case 521:
c.KeyAlgorithm = CertificateKeyAlgorithmTypeEC512
default:
c.KeyAlgorithm = CertificateKeyAlgorithmType(fmt.Sprintf("EC%d", len))
}
}
case x509.Ed25519:
{
c.KeyAlgorithm = CertificateKeyAlgorithmType("ED25519")
}
default:
c.KeyAlgorithm = CertificateKeyAlgorithmType("")
}
@@ -63,10 +105,10 @@ func (c *Certificate) PopulateFromPEM(certPEM, privkeyPEM string) *Certificate {
c.Certificate = certPEM
c.PrivateKey = privkeyPEM
_, issuerCertPEM, _ := certs.ExtractCertificatesFromPEM(certPEM)
_, issuerCertPEM, _ := certutil.ExtractCertificatesFromPEM(certPEM)
c.IssuerCertificate = issuerCertPEM
certX509, _ := certs.ParseCertificateFromPEM(certPEM)
certX509, _ := certutil.ParseCertificateFromPEM(certPEM)
if certX509 != nil {
c.PopulateFromX509(certX509)
}

View File

@@ -12,7 +12,11 @@ const (
NotifyChannelTypeBark = NotifyChannelType("bark")
NotifyChannelTypeDingTalk = NotifyChannelType("dingtalk")
NotifyChannelTypeEmail = NotifyChannelType("email")
NotifyChannelTypeGotify = NotifyChannelType("gotify")
NotifyChannelTypeLark = NotifyChannelType("lark")
NotifyChannelTypeMattermost = NotifyChannelType("mattermost")
NotifyChannelTypePushover = NotifyChannelType("pushover")
NotifyChannelTypePushPlus = NotifyChannelType("pushplus")
NotifyChannelTypeServerChan = NotifyChannelType("serverchan")
NotifyChannelTypeTelegram = NotifyChannelType("telegram")
NotifyChannelTypeWebhook = NotifyChannelType("webhook")

View File

@@ -9,50 +9,80 @@ type AccessProviderType string
NOTICE: If you add new constant, please keep ASCII order.
*/
const (
AccessProviderType1Panel = AccessProviderType("1panel") // 1Panel预留
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai预留
AccessProviderTypeAliyun = AccessProviderType("aliyun")
AccessProviderTypeAWS = AccessProviderType("aws")
AccessProviderTypeAzure = AccessProviderType("azure")
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeEdgio = AccessProviderType("edgio")
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly预留
AccessProviderTypeGname = AccessProviderType("gname")
AccessProviderTypeGcore = AccessProviderType("gcore")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge预留
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
AccessProviderTypeKubernetes = AccessProviderType("k8s")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
AccessProviderTypeNS1 = AccessProviderType("ns1")
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
AccessProviderTypeRainYun = AccessProviderType("rainyun")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSSH = AccessProviderType("ssh")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeWebhook = AccessProviderType("webhook")
AccessProviderTypeWestcn = AccessProviderType("westcn")
AccessProviderType1Panel = AccessProviderType("1panel")
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai预留
AccessProviderTypeAliyun = AccessProviderType("aliyun")
AccessProviderTypeAWS = AccessProviderType("aws")
AccessProviderTypeAzure = AccessProviderType("azure")
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
AccessProviderTypeBuypass = AccessProviderType("buypass")
AccessProviderTypeCacheFly = AccessProviderType("cachefly")
AccessProviderTypeCdnfly = AccessProviderType("cdnfly")
AccessProviderTypeCloudflare = AccessProviderType("cloudflare")
AccessProviderTypeClouDNS = AccessProviderType("cloudns")
AccessProviderTypeCMCCCloud = AccessProviderType("cmcccloud")
AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 联通云(预留)
AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 天翼云(预留)
AccessProviderTypeDeSEC = AccessProviderType("desec")
AccessProviderTypeDNSLA = AccessProviderType("dnsla")
AccessProviderTypeDogeCloud = AccessProviderType("dogecloud")
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
AccessProviderTypeEdgio = AccessProviderType("edgio")
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly预留
AccessProviderTypeGname = AccessProviderType("gname")
AccessProviderTypeGcore = AccessProviderType("gcore")
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge预留
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
AccessProviderTypeKubernetes = AccessProviderType("k8s")
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
AccessProviderTypeNameSilo = AccessProviderType("namesilo")
AccessProviderTypeNS1 = AccessProviderType("ns1")
AccessProviderTypePorkbun = AccessProviderType("porkbun")
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
AccessProviderTypeQiniu = AccessProviderType("qiniu")
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
AccessProviderTypeRainYun = AccessProviderType("rainyun")
AccessProviderTypeSafeLine = AccessProviderType("safeline")
AccessProviderTypeSSH = AccessProviderType("ssh")
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
AccessProviderTypeUCloud = AccessProviderType("ucloud")
AccessProviderTypeUpyun = AccessProviderType("upyun")
AccessProviderTypeVercel = AccessProviderType("vercel")
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeWangsu = AccessProviderType("wangsu")
AccessProviderTypeWebhook = AccessProviderType("webhook")
AccessProviderTypeWestcn = AccessProviderType("westcn")
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
)
type ApplyCAProviderType 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
@@ -77,7 +107,9 @@ const (
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")
@@ -89,10 +121,13 @@ const (
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")
@@ -101,26 +136,35 @@ const (
type DeployProviderType string
/*
部署目标提供商常量值。
部署证书主机提供商常量值。
短横线前的部分始终等于授权提供商类型。
注意:如果追加新的常量值,请保持以 ASCII 排序。
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")
@@ -132,6 +176,7 @@ const (
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")
@@ -140,7 +185,9 @@ const (
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")
@@ -149,16 +196,23 @@ const (
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")
)

View File

@@ -14,12 +14,10 @@ type Settings struct {
}
type NotifyTemplatesSettingsContent struct {
NotifyTemplates []NotifyTemplate `json:"notifyTemplates"`
}
type NotifyTemplate struct {
Subject string `json:"subject"`
Message string `json:"message"`
NotifyTemplates []struct {
Subject string `json:"subject"`
Message string `json:"message"`
} `json:"notifyTemplates"`
}
type NotifyChannelsSettingsContent map[string]map[string]any
@@ -37,3 +35,8 @@ func (s *Settings) GetNotifyChannelConfig(channel string) (map[string]any, error
return v, nil
}
type PersistenceSettingsContent struct {
WorkflowRunsMaxDaysRetention int `json:"workflowRunsMaxDaysRetention"`
ExpiredCertificatesMaxDaysRetention int `json:"expiredCertificatesMaxDaysRetention"`
}

View File

@@ -3,7 +3,7 @@ package domain
import (
"time"
"github.com/usual2970/certimate/internal/pkg/utils/maps"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
)
const CollectionNameWorkflow = "workflow"
@@ -62,19 +62,22 @@ type WorkflowNode struct {
}
type WorkflowNodeConfigForApply struct {
Domains string `json:"domains"` // 域名列表,以半角号分隔
ContactEmail string `json:"contactEmail"` // 联系邮箱
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
Provider string `json:"provider"` // DNS 提供商
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
KeyAlgorithm string `json:"keyAlgorithm"` // 密钥算法
Nameservers string `json:"nameservers"` // DNS 服务器列表,以半角逗号分隔
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout"` // DNS 传播超时时间(零值取决于提供商的默认值)
DnsTTL int32 `json:"dnsTTL"` // DNS TTL零值取决于提供商的默认值
DisableFollowCNAME bool `json:"disableFollowCNAME"` // 是否关闭 CNAME 跟随
DisableARI bool `json:"disableARI"` // 是否关闭 ARI
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays"` // 证书到期前多少天前跳过续期(零值将使用默认值 30
Domains string `json:"domains"` // 域名列表,以半角号分隔
ContactEmail string `json:"contactEmail"` // 联系邮箱
ChallengeType string `json:"challengeType"` // TODO: 验证方式。目前仅支持 dns-01
Provider string `json:"provider"` // DNS 提供商
ProviderAccessId string `json:"providerAccessId"` // DNS 提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig"` // DNS 提供商额外配置
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"` // 密钥算法
Nameservers string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播超时时间(零值取决于提供商的默认值)
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS TTL零值取决于提供商的默认值
DisableFollowCNAME bool `json:"disableFollowCNAME,omitempty"` // 是否关闭 CNAME 跟随
DisableARI bool `json:"disableARI,omitempty"` // 是否关闭 ARI
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays,omitempty"` // 证书到期前多少天前跳过续期(零值将使用默认值 30
}
type WorkflowNodeConfigForUpload struct {
@@ -84,11 +87,11 @@ type WorkflowNodeConfigForUpload struct {
}
type WorkflowNodeConfigForDeploy struct {
Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate”
Provider string `json:"provider"` // 主机提供商
ProviderAccessId string `json:"providerAccessId"` // 主机提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig"` // 主机提供商额外配置
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过
Certificate string `json:"certificate"` // 前序节点输出的证书,形如“${NodeId}#certificate”
Provider string `json:"provider"` // 主机提供商
ProviderAccessId string `json:"providerAccessId,omitempty"` // 主机提供商授权记录 ID
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 主机提供商额外配置
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过
}
type WorkflowNodeConfigForNotify struct {
@@ -97,73 +100,54 @@ type WorkflowNodeConfigForNotify struct {
Message string `json:"message"` // 通知内容
}
func (n *WorkflowNode) getConfigValueAsString(key string) string {
return maps.GetValueAsString(n.Config, key)
}
func (n *WorkflowNode) getConfigValueAsBool(key string) bool {
return maps.GetValueAsBool(n.Config, key)
}
func (n *WorkflowNode) getConfigValueAsInt32(key string) int32 {
return maps.GetValueAsInt32(n.Config, key)
}
func (n *WorkflowNode) getConfigValueAsMap(key string) map[string]any {
if val, ok := n.Config[key]; ok {
if result, ok := val.(map[string]any); ok {
return result
}
}
return make(map[string]any)
}
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
skipBeforeExpiryDays := n.getConfigValueAsInt32("skipBeforeExpiryDays")
skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
if skipBeforeExpiryDays == 0 {
skipBeforeExpiryDays = 30
}
return WorkflowNodeConfigForApply{
Domains: n.getConfigValueAsString("domains"),
ContactEmail: n.getConfigValueAsString("contactEmail"),
Provider: n.getConfigValueAsString("provider"),
ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
ProviderConfig: n.getConfigValueAsMap("providerConfig"),
KeyAlgorithm: n.getConfigValueAsString("keyAlgorithm"),
Nameservers: n.getConfigValueAsString("nameservers"),
DnsPropagationTimeout: n.getConfigValueAsInt32("dnsPropagationTimeout"),
DnsTTL: n.getConfigValueAsInt32("dnsTTL"),
DisableFollowCNAME: n.getConfigValueAsBool("disableFollowCNAME"),
DisableARI: n.getConfigValueAsBool("disableARI"),
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"),
CAProvider: maputil.GetString(n.Config, "caProvider"),
CAProviderAccessId: maputil.GetString(n.Config, "caProviderAccessId"),
CAProviderConfig: maputil.GetAnyMap(n.Config, "caProviderConfig"),
KeyAlgorithm: maputil.GetString(n.Config, "keyAlgorithm"),
Nameservers: maputil.GetString(n.Config, "nameservers"),
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,
}
}
func (n *WorkflowNode) GetConfigForUpload() WorkflowNodeConfigForUpload {
return WorkflowNodeConfigForUpload{
Certificate: n.getConfigValueAsString("certificate"),
PrivateKey: n.getConfigValueAsString("privateKey"),
Domains: n.getConfigValueAsString("domains"),
Certificate: maputil.GetString(n.Config, "certificate"),
PrivateKey: maputil.GetString(n.Config, "privateKey"),
Domains: maputil.GetString(n.Config, "domains"),
}
}
func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
return WorkflowNodeConfigForDeploy{
Certificate: n.getConfigValueAsString("certificate"),
Provider: n.getConfigValueAsString("provider"),
ProviderAccessId: n.getConfigValueAsString("providerAccessId"),
ProviderConfig: n.getConfigValueAsMap("providerConfig"),
SkipOnLastSucceeded: n.getConfigValueAsBool("skipOnLastSucceeded"),
Certificate: maputil.GetString(n.Config, "certificate"),
Provider: maputil.GetString(n.Config, "provider"),
ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
ProviderConfig: maputil.GetAnyMap(n.Config, "providerConfig"),
SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
}
}
func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
return WorkflowNodeConfigForNotify{
Channel: n.getConfigValueAsString("channel"),
Subject: n.getConfigValueAsString("subject"),
Message: n.getConfigValueAsString("message"),
Channel: maputil.GetString(n.Config, "channel"),
Subject: maputil.GetString(n.Config, "subject"),
Message: maputil.GetString(n.Config, "message"),
}
}

View File

@@ -0,0 +1,30 @@
package domain
import "strings"
const CollectionNameWorkflowLog = "workflow_logs"
type WorkflowLog struct {
Meta
WorkflowId string `json:"workflowId" db:"workflowId"`
RunId string `json:"workflorunIdwId" db:"runId"`
NodeId string `json:"nodeId" db:"nodeId"`
NodeName string `json:"nodeName" db:"nodeName"`
Timestamp int64 `json:"timestamp" db:"timestamp"` // 毫秒级时间戳
Level string `json:"level" db:"level"`
Message string `json:"message" db:"message"`
Data map[string]any `json:"data" db:"data"`
}
type WorkflowLogs []WorkflowLog
func (r WorkflowLogs) ErrorString() string {
var builder strings.Builder
for _, log := range r {
if log.Level == "ERROR" {
builder.WriteString(log.Message)
builder.WriteString("\n")
}
}
return strings.TrimSpace(builder.String())
}

View File

@@ -1,7 +1,6 @@
package domain
import (
"strings"
"time"
)
@@ -14,7 +13,7 @@ type WorkflowRun struct {
Trigger WorkflowTriggerType `json:"trigger" db:"trigger"`
StartedAt time.Time `json:"startedAt" db:"startedAt"`
EndedAt time.Time `json:"endedAt" db:"endedAt"`
Logs []WorkflowRunLog `json:"logs" db:"logs"`
Detail *WorkflowNode `json:"detail" db:"detail"`
Error string `json:"error" db:"error"`
}
@@ -27,39 +26,3 @@ const (
WorkflowRunStatusTypeFailed WorkflowRunStatusType = "failed"
WorkflowRunStatusTypeCanceled WorkflowRunStatusType = "canceled"
)
type WorkflowRunLog struct {
NodeId string `json:"nodeId"`
NodeName string `json:"nodeName"`
Records []WorkflowRunLogRecord `json:"records"`
Error string `json:"error"`
}
type WorkflowRunLogRecord struct {
Time string `json:"time"`
Level WorkflowRunLogLevel `json:"level"`
Content string `json:"content"`
Error string `json:"error"`
}
type WorkflowRunLogLevel string
const (
WorkflowRunLogLevelDebug WorkflowRunLogLevel = "DEBUG"
WorkflowRunLogLevelInfo WorkflowRunLogLevel = "INFO"
WorkflowRunLogLevelWarn WorkflowRunLogLevel = "WARN"
WorkflowRunLogLevelError WorkflowRunLogLevel = "ERROR"
)
type WorkflowRunLogs []WorkflowRunLog
func (r WorkflowRunLogs) ErrorString() string {
var builder strings.Builder
for _, log := range r {
if log.Error != "" {
builder.WriteString(log.Error)
builder.WriteString("\n")
}
}
return builder.String()
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
"github.com/usual2970/certimate/internal/pkg/utils/maps"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
"github.com/usual2970/certimate/internal/repository"
)
@@ -62,7 +62,7 @@ func getEnabledNotifiers() ([]notifier.Notifier, error) {
notifiers := make([]notifier.Notifier, 0)
for k, v := range rs {
if !maps.GetValueAsBool(v, "enabled") {
if !maputil.GetBool(v, "enabled") {
continue
}

View File

@@ -8,12 +8,16 @@ import (
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
"github.com/usual2970/certimate/internal/pkg/utils/maps"
"github.com/usual2970/certimate/internal/pkg/utils/maputil"
)
func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]any) (notifier.Notifier, error) {
@@ -24,51 +28,77 @@ func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]a
switch channel {
case domain.NotifyChannelTypeBark:
return pBark.NewNotifier(&pBark.NotifierConfig{
DeviceKey: maps.GetValueAsString(channelConfig, "deviceKey"),
ServerUrl: maps.GetValueAsString(channelConfig, "serverUrl"),
DeviceKey: maputil.GetString(channelConfig, "deviceKey"),
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
})
case domain.NotifyChannelTypeDingTalk:
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
AccessToken: maps.GetValueAsString(channelConfig, "accessToken"),
Secret: maps.GetValueAsString(channelConfig, "secret"),
AccessToken: maputil.GetString(channelConfig, "accessToken"),
Secret: maputil.GetString(channelConfig, "secret"),
})
case domain.NotifyChannelTypeEmail:
return pEmail.NewNotifier(&pEmail.NotifierConfig{
SmtpHost: maps.GetValueAsString(channelConfig, "smtpHost"),
SmtpPort: maps.GetValueAsInt32(channelConfig, "smtpPort"),
SmtpTLS: maps.GetValueOrDefaultAsBool(channelConfig, "smtpTLS", true),
Username: maps.GetValueOrDefaultAsString(channelConfig, "username", maps.GetValueAsString(channelConfig, "senderAddress")),
Password: maps.GetValueAsString(channelConfig, "password"),
SenderAddress: maps.GetValueAsString(channelConfig, "senderAddress"),
ReceiverAddress: maps.GetValueAsString(channelConfig, "receiverAddress"),
SmtpHost: maputil.GetString(channelConfig, "smtpHost"),
SmtpPort: maputil.GetInt32(channelConfig, "smtpPort"),
SmtpTLS: maputil.GetOrDefaultBool(channelConfig, "smtpTLS", true),
Username: maputil.GetOrDefaultString(channelConfig, "username", maputil.GetString(channelConfig, "senderAddress")),
Password: maputil.GetString(channelConfig, "password"),
SenderAddress: maputil.GetString(channelConfig, "senderAddress"),
ReceiverAddress: maputil.GetString(channelConfig, "receiverAddress"),
})
case domain.NotifyChannelTypeGotify:
return pGotify.NewNotifier(&pGotify.NotifierConfig{
Url: maputil.GetString(channelConfig, "url"),
Token: maputil.GetString(channelConfig, "token"),
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
})
case domain.NotifyChannelTypeLark:
return pLark.NewNotifier(&pLark.NotifierConfig{
WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"),
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
})
case domain.NotifyChannelTypeMattermost:
return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
ChannelId: maputil.GetString(channelConfig, "channelId"),
Username: maputil.GetString(channelConfig, "username"),
Password: maputil.GetString(channelConfig, "password"),
})
case domain.NotifyChannelTypePushover:
return pPushover.NewNotifier(&pPushover.NotifierConfig{
Token: maputil.GetString(channelConfig, "token"),
User: maputil.GetString(channelConfig, "user"),
})
case domain.NotifyChannelTypePushPlus:
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
Token: maputil.GetString(channelConfig, "token"),
})
case domain.NotifyChannelTypeServerChan:
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
Url: maps.GetValueAsString(channelConfig, "url"),
Url: maputil.GetString(channelConfig, "url"),
})
case domain.NotifyChannelTypeTelegram:
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
ApiToken: maps.GetValueAsString(channelConfig, "apiToken"),
ChatId: maps.GetValueAsInt64(channelConfig, "chatId"),
ApiToken: maputil.GetString(channelConfig, "apiToken"),
ChatId: maputil.GetInt64(channelConfig, "chatId"),
})
case domain.NotifyChannelTypeWebhook:
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
Url: maps.GetValueAsString(channelConfig, "url"),
Url: maputil.GetString(channelConfig, "url"),
AllowInsecureConnections: maputil.GetBool(channelConfig, "allowInsecureConnections"),
})
case domain.NotifyChannelTypeWeCom:
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
WebhookUrl: maps.GetValueAsString(channelConfig, "webhookUrl"),
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
})
}

View File

@@ -6,7 +6,7 @@ import (
"strings"
"time"
bceDns "github.com/baidubce/bce-sdk-go/services/dns"
bcedns "github.com/baidubce/bce-sdk-go/services/dns"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
@@ -38,7 +38,7 @@ type Config struct {
}
type DNSProvider struct {
client *bceDns.Client
client *bcedns.Client
config *Config
}
@@ -69,7 +69,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("baiducloud: the configuration of the DNS provider is nil")
}
client, err := bceDns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
client, err := bcedns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
if err != nil {
return nil, err
} else {
@@ -128,11 +128,11 @@ 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) getDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) {
pageMarker := ""
pageSize := 1000
for {
request := &bceDns.ListRecordRequest{}
request := &bcedns.ListRecordRequest{}
request.Rr = subDomain
request.Marker = pageMarker
request.MaxKeys = pageSize
@@ -165,7 +165,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
}
if record == nil {
request := &bceDns.CreateRecordRequest{
request := &bcedns.CreateRecordRequest{
Type: "TXT",
Rr: subDomain,
Value: value,
@@ -174,7 +174,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
err := d.client.CreateRecord(zoneName, request, d.generateClientToken())
return err
} else {
request := &bceDns.UpdateRecordRequest{
request := &bcedns.UpdateRecordRequest{
Type: "TXT",
Rr: subDomain,
Value: value,

View File

@@ -9,6 +9,7 @@ import (
type ChallengeProviderConfig struct {
DnsApiToken string `json:"dnsApiToken"`
ZoneApiToken string `json:"zoneApiToken,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
@@ -20,6 +21,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
providerConfig := cloudflare.NewDefaultConfig()
providerConfig.AuthToken = config.DnsApiToken
providerConfig.ZoneToken = config.ZoneApiToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}

View File

@@ -106,6 +106,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
if err != nil {
return err
}
if record == nil {
// add new record
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{

View File

@@ -0,0 +1,36 @@
package desec
import (
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/desec"
)
type ChallengeProviderConfig struct {
Token string `json:"token"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
if config == nil {
panic("config is nil")
}
providerConfig := desec.NewDefaultConfig()
providerConfig.Token = config.Token
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := desec.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,37 @@
package dynv6
import (
"time"
"github.com/go-acme/lego/v4/challenge"
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6/internal"
)
type ChallengeProviderConfig struct {
HttpToken string `json:"httpToken"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
if config == nil {
panic("config is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.HTTPToken = config.HttpToken
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,167 @@
package lego_dynv6
import (
"context"
"errors"
"fmt"
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
"github.com/libdns/dynv6"
"github.com/libdns/libdns"
)
const (
envNamespace = "DYNV6_"
EnvHTTPToken = envNamespace + "HTTP_TOKEN"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
HTTPToken string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
}
type DNSProvider struct {
client *dynv6.Provider
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
}
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvHTTPToken)
if err != nil {
return nil, fmt.Errorf("dynv6: %w", err)
}
config := NewDefaultConfig()
config.HTTPToken = values[EnvHTTPToken]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("dynv6: the configuration of the DNS provider is nil")
}
client := &dynv6.Provider{Token: config.HTTPToken}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
return fmt.Errorf("dynv6: %w", err)
}
return nil
}
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("dynv6: %w", err)
}
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
return fmt.Errorf("dynv6: %w", err)
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*libdns.Record, error) {
records, err := d.client.GetRecords(context.Background(), zoneName)
if err != nil {
return nil, err
}
for _, record := range records {
if record.Type == "TXT" && record.Name == subDomain {
return &record, nil
}
}
return nil, nil
}
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
if err != nil {
return err
}
if record == nil {
record = &libdns.Record{
Type: "TXT",
Name: subDomain,
Value: value,
TTL: time.Duration(d.config.TTL) * time.Second,
}
_, err := d.client.AppendRecords(context.Background(), zoneName, []libdns.Record{*record})
return err
} else {
record.Value = value
_, err := d.client.SetRecords(context.Background(), zoneName, []libdns.Record{*record})
return err
}
}
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
record, err := d.getDNSRecord(zoneName, subDomain)
if err != nil {
return err
}
if record == nil {
return nil
} else {
_, err = d.client.DeleteRecords(context.Background(), zoneName, []libdns.Record{*record})
return err
}
}

View File

@@ -122,8 +122,8 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
}
func (d *DNSProvider) getDNSRecord(zoneName, subDomain string) (*gnamesdk.ResolutionRecord, error) {
page := 1
pageSize := 20
page := int32(1)
pageSize := int32(20)
for {
request := &gnamesdk.ListDomainResolutionRequest{}
request.ZoneName = zoneName
@@ -166,18 +166,19 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
RecordType: "TXT",
RecordName: subDomain,
RecordValue: value,
TTL: d.config.TTL,
TTL: int32(d.config.TTL),
}
_, err := d.client.AddDomainResolution(request)
return err
} else {
recordId, _ := record.ID.Int64()
request := &gnamesdk.ModifyDomainResolutionRequest{
ID: record.ID,
ID: recordId,
ZoneName: zoneName,
RecordType: "TXT",
RecordName: subDomain,
RecordValue: value,
TTL: d.config.TTL,
TTL: int32(d.config.TTL),
}
_, err := d.client.ModifyDomainResolution(request)
return err
@@ -194,9 +195,10 @@ func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
return nil
}
recordId, _ := record.ID.Int64()
request := &gnamesdk.DeleteDomainResolutionRequest{
ZoneName: zoneName,
RecordID: record.ID,
RecordID: recordId,
}
_, err = d.client.DeleteDomainResolution(request)
return err

View File

@@ -8,10 +8,10 @@ import (
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdDnsApi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
jdDnsClient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
jdDnsModel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jddnsapi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
jddnsclient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
jddnsmodel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
)
const (
@@ -41,7 +41,7 @@ type Config struct {
}
type DNSProvider struct {
client *jdDnsClient.DomainserviceClient
client *jddnsclient.DomainserviceClient
config *Config
}
@@ -73,12 +73,12 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
}
clientCredentials := jdCore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
client := jdDnsClient.NewDomainserviceClient(clientCredentials)
clientCredentials := jdcore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
client := jddnsclient.NewDomainserviceClient(clientCredentials)
clientConfig := &client.Config
clientConfig.SetTimeout(config.HTTPTimeout)
client.SetConfig(clientConfig)
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return &DNSProvider{
client: client,
@@ -130,11 +130,11 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error) {
func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error) {
pageNumber := 1
pageSize := 10
for {
request := jdDnsApi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
request := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
request.SetDomainName(zoneName)
response, err := d.client.DescribeDomains(request)
@@ -158,7 +158,7 @@ func (d *DNSProvider) getDNSZone(zoneName string) (*jdDnsModel.DomainInfo, error
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
}
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) {
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jddnsmodel.DomainInfo, *jddnsmodel.RRInfo, error) {
zone, err := d.getDNSZone(zoneName)
if err != nil {
return nil, nil, err
@@ -167,7 +167,7 @@ func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jdDnsMod
pageNumber := 1
pageSize := 10
for {
request := jdDnsApi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
request := jddnsapi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
request.SetSearch(subDomain)
request.SetPageNumber(pageNumber)
request.SetPageSize(pageSize)
@@ -200,7 +200,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
}
if record == nil {
request := jdDnsApi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jdDnsModel.AddRR{
request := jddnsapi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jddnsmodel.AddRR{
Type: "TXT",
HostRecord: subDomain,
HostValue: value,
@@ -210,7 +210,7 @@ func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) er
_, err := d.client.CreateResourceRecord(request)
return err
} else {
request := jdDnsApi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jdDnsModel.UpdateRR{
request := jddnsapi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jddnsmodel.UpdateRR{
Type: "TXT",
HostRecord: subDomain,
HostValue: value,
@@ -231,7 +231,7 @@ func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
if record == nil {
return nil
} else {
request := jdDnsApi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
request := jddnsapi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
_, err = d.client.DeleteResourceRecord(request)
return err
}

View File

@@ -0,0 +1,38 @@
package porkbun
import (
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/porkbun"
)
type ChallengeProviderConfig struct {
ApiKey string `json:"apiKey"`
SecretApiKey string `json:"secretApiKey"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
if config == nil {
panic("config is nil")
}
providerConfig := porkbun.NewDefaultConfig()
providerConfig.APIKey = config.ApiKey
providerConfig.SecretAPIKey = config.SecretApiKey
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := porkbun.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

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

View File

@@ -0,0 +1,207 @@
package lego_tencentcloudeo
import (
"errors"
"fmt"
"math"
"strings"
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/platform/config/env"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
teo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
)
const (
envNamespace = "TENCENTCLOUDEO_"
EnvSecretID = envNamespace + "SECRET_ID"
EnvSecretKey = envNamespace + "SECRET_KEY"
EnvZoneId = envNamespace + "ZONE_ID"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
)
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
type Config struct {
SecretID string
SecretKey string
ZoneId string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int32
HTTPTimeout time.Duration
}
type DNSProvider struct {
client *teo.Client
config *Config
}
func NewDefaultConfig() *Config {
return &Config{
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
}
}
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneId)
if err != nil {
return nil, fmt.Errorf("tencentcloud-eo: %w", err)
}
config := NewDefaultConfig()
config.SecretID = values[EnvSecretID]
config.SecretKey = values[EnvSecretKey]
config.ZoneId = values[EnvSecretKey]
return NewDNSProviderConfig(config)
}
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("tencentcloud-eo: the configuration of the DNS provider is nil")
}
credential := common.NewCredential(config.SecretID, config.SecretKey)
cpf := profile.NewClientProfile()
cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds()))
client, err := teo.NewClient(credential, "", cpf)
if err != nil {
return nil, err
}
return &DNSProvider{
client: client,
config: config,
}, nil
}
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
if err := d.addOrUpdateDNSRecord(strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil {
return fmt.Errorf("tencentcloud-eo: %w", err)
}
return nil
}
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
if err := d.removeDNSRecord(strings.TrimRight(info.EffectiveFQDN, ".")); err != nil {
return fmt.Errorf("tencentcloud-eo: %w", err)
}
return nil
}
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
func (d *DNSProvider) getDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) {
pageOffset := 0
pageLimit := 1000
for {
request := teo.NewDescribeDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.Offset = common.Int64Ptr(int64(pageOffset))
request.Limit = common.Int64Ptr(int64(pageLimit))
request.Filters = []*teo.AdvancedFilter{
{
Name: common.StringPtr("type"),
Values: []*string{common.StringPtr("TXT")},
},
}
response, err := d.client.DescribeDnsRecords(request)
if err != nil {
return nil, err
}
if response.Response == nil {
break
} else {
for _, record := range response.Response.DnsRecords {
if *record.Name == effectiveFQDN {
return record, nil
}
}
if len(response.Response.DnsRecords) < int(pageLimit) {
break
}
pageOffset += len(response.Response.DnsRecords)
}
}
return nil, nil
}
func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
record, err := d.getDNSRecord(effectiveFQDN)
if err != nil {
return err
}
if record == nil {
request := teo.NewCreateDnsRecordRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.Name = common.StringPtr(effectiveFQDN)
request.Type = common.StringPtr("TXT")
request.Content = common.StringPtr(value)
request.TTL = common.Int64Ptr(int64(d.config.TTL))
_, err := d.client.CreateDnsRecord(request)
return err
} else {
record.Content = common.StringPtr(value)
request := teo.NewModifyDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.DnsRecords = []*teo.DnsRecord{record}
if _, err := d.client.ModifyDnsRecords(request); err != nil {
return err
}
if *record.Status == "disable" {
request := teo.NewModifyDnsRecordsStatusRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.RecordsToEnable = []*string{record.RecordId}
if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil {
return err
}
}
return nil
}
}
func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error {
record, err := d.getDNSRecord(effectiveFQDN)
if err != nil {
return err
}
if record == nil {
return nil
} else {
request := teo.NewDeleteDnsRecordsRequest()
request.ZoneId = common.StringPtr(d.config.ZoneId)
request.RecordIds = []*string{record.RecordId}
_, err = d.client.DeleteDnsRecords(request)
return err
}
}

View File

@@ -0,0 +1,41 @@
package tencentcloudeo
import (
"time"
"github.com/go-acme/lego/v4/challenge"
internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/tencentcloud-eo/internal"
)
type ChallengeProviderConfig struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
ZoneId string `json:"zoneId"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
if config == nil {
panic("config is nil")
}
providerConfig := internal.NewDefaultConfig()
providerConfig.SecretID = config.SecretId
providerConfig.SecretKey = config.SecretKey
providerConfig.ZoneId = config.ZoneId
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = config.DnsTTL
}
provider, err := internal.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -0,0 +1,38 @@
package vercel
import (
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/vercel"
)
type ChallengeProviderConfig struct {
ApiAccessToken string `json:"apiAccessToken"`
TeamId string `json:"teamId,omitempty"`
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
DnsTTL int32 `json:"dnsTTL,omitempty"`
}
func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
if config == nil {
panic("config is nil")
}
providerConfig := vercel.NewDefaultConfig()
providerConfig.AuthToken = config.ApiAccessToken
providerConfig.TeamID = config.TeamId
if config.DnsPropagationTimeout != 0 {
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
}
if config.DnsTTL != 0 {
providerConfig.TTL = int(config.DnsTTL)
}
provider, err := vercel.NewDNSProviderConfig(providerConfig)
if err != nil {
return nil, err
}
return provider, nil
}

View File

@@ -1,10 +1,15 @@
package deployer
import "context"
import (
"context"
"log/slog"
)
// 表示定义证书部署器的抽象类型接口。
// 注意与 `Uploader` 区分,“部署”通常为“上传”的后置操作。
type Deployer interface {
WithLogger(logger *slog.Logger) Deployer
// 部署证书。
//
// 入参:

View File

@@ -0,0 +1,98 @@
package onepanelconsole
import (
"context"
"crypto/tls"
"errors"
"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"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
// 是否自动重启。
AutoRestart bool `json:"autoRestart"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *opsdk.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sdkClient: client,
}, 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) {
// 设置面板 SSL 证书
updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
Cert: certPem,
Key: privkeyPem,
SSL: "enable",
SSLType: "import-paste",
}
if d.config.AutoRestart {
updateSystemSSLReq.AutoRestart = "true"
} else {
updateSystemSSLReq.AutoRestart = "false"
}
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 &deployer.DeployResult{}, nil
}
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
client := opsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}
return client, nil
}

View File

@@ -0,0 +1,72 @@
package onepanelconsole_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-console"
)
var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
fApiKey string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
}
/*
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"
*/
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("APIURL: %v", fApiUrl),
fmt.Sprintf("APIKEY: %v", fApiKey),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiKey: fApiKey,
AllowInsecureConnections: true,
AutoRestart: true,
})
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,130 @@
package onepanelsite
import (
"context"
"crypto/tls"
"errors"
"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"
)
type DeployerConfig struct {
// 1Panel 地址。
ApiUrl string `json:"apiUrl"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
// 网站 ID。
WebsiteId int64 `json:"websiteId"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *opsdk.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.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
ApiUrl: config.ApiUrl,
ApiKey: config.ApiKey,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
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) {
// 获取网站 HTTPS 配置
getHttpsConfReq := &opsdk.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'")
}
// 上传证书到面板
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 修改网站 HTTPS 配置
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
WebsiteID: d.config.WebsiteId,
Type: "existed",
WebsiteSSLID: certId,
Enable: getHttpsConfResp.Data.Enable,
HttpConfig: getHttpsConfResp.Data.HttpConfig,
SSLProtocol: getHttpsConfResp.Data.SSLProtocol,
Algorithm: getHttpsConfResp.Data.Algorithm,
Hsts: getHttpsConfResp.Data.Hsts,
}
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 &deployer.DeployResult{}, nil
}
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*opsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
if apiKey == "" {
return nil, errors.New("invalid 1panel api key")
}
client := opsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}
return client, nil
}

View File

@@ -0,0 +1,76 @@
package onepanelsite_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-site"
)
var (
fInputCertPath string
fInputKeyPath string
fApiUrl string
fApiKey string
fWebsiteId int64
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
}
/*
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"
*/
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("APIURL: %v", fApiUrl),
fmt.Sprintf("APIKEY: %v", fApiKey),
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiKey: fApiKey,
WebsiteId: fWebsiteId,
AllowInsecureConnections: true,
})
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

@@ -4,19 +4,19 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strconv"
"strings"
"time"
aliyunAlb "github.com/alibabacloud-go/alb-20200616/v2/client"
aliyunCas "github.com/alibabacloud-go/cas-20200407/v3/client"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
alialb "github.com/alibabacloud-go/alb-20200616/v2/client"
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"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
)
@@ -43,7 +43,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClients *wSdkClients
sslUploader uploader.Uploader
}
@@ -51,8 +51,8 @@ type DeployerProvider struct {
var _ deployer.Deployer = (*DeployerProvider)(nil)
type wSdkClients struct {
alb *aliyunAlb.Client
cas *aliyunCas.Client
ALB *alialb.Client
CAS *alicas.Client
}
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
@@ -72,14 +72,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClients: clients,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -88,10 +93,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
@@ -118,29 +123,29 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
// 查询负载均衡实例的详细信息
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
getLoadBalancerAttributeReq := &aliyunAlb.GetLoadBalancerAttributeRequest{
getLoadBalancerAttributeReq := &alialb.GetLoadBalancerAttributeRequest{
LoadBalancerId: tea.String(d.config.LoadbalancerId),
}
getLoadBalancerAttributeResp, err := d.sdkClients.alb.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
d.logger.Debug("sdk request 'alb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetLoadBalancerAttribute'")
}
d.logger.Logt("已查询到 ALB 负载均衡实例", getLoadBalancerAttributeResp)
// 查询 HTTPS 监听列表
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
listenerIds := make([]string, 0)
listListenersLimit := int32(100)
var listListenersToken *string = nil
for {
listListenersReq := &aliyunAlb.ListListenersRequest{
listListenersReq := &alialb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
ListenerProtocol: tea.String("HTTPS"),
}
listListenersResp, err := d.sdkClients.alb.ListListeners(listListenersReq)
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
}
@@ -158,19 +163,18 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
}
}
d.logger.Logt("已查询到 ALB 负载均衡实例下的全部 HTTPS 监听", listenerIds)
// 查询 QUIC 监听列表
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
listListenersToken = nil
for {
listListenersReq := &aliyunAlb.ListListenersRequest{
listListenersReq := &alialb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
ListenerProtocol: tea.String("QUIC"),
}
listListenersResp, err := d.sdkClients.alb.ListListeners(listListenersReq)
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListeners'")
}
@@ -188,13 +192,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
}
}
d.logger.Logt("已查询到 ALB 负载均衡实例下的全部 QUIC 监听", listenerIds)
// 遍历更新监听证书
if len(listenerIds) == 0 {
return errors.New("listener not found")
d.logger.Info("no alb listeners to deploy")
} else {
var errs []error
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 {
@@ -226,49 +229,48 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
// 查询监听的属性
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
getListenerAttributeReq := &aliyunAlb.GetListenerAttributeRequest{
getListenerAttributeReq := &alialb.GetListenerAttributeRequest{
ListenerId: tea.String(cloudListenerId),
}
getListenerAttributeResp, err := d.sdkClients.alb.GetListenerAttribute(getListenerAttributeReq)
getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttribute(getListenerAttributeReq)
d.logger.Debug("sdk request 'alb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.GetListenerAttribute'")
}
d.logger.Logt("已查询到 ALB 监听配置", getListenerAttributeResp)
if d.config.Domain == "" {
// 未指定 SNI只需部署到监听器
// 修改监听的属性
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
updateListenerAttributeReq := &aliyunAlb.UpdateListenerAttributeRequest{
updateListenerAttributeReq := &alialb.UpdateListenerAttributeRequest{
ListenerId: tea.String(cloudListenerId),
Certificates: []*aliyunAlb.UpdateListenerAttributeRequestCertificates{{
Certificates: []*alialb.UpdateListenerAttributeRequestCertificates{{
CertificateId: tea.String(cloudCertId),
}},
}
updateListenerAttributeResp, err := d.sdkClients.alb.UpdateListenerAttribute(updateListenerAttributeReq)
updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttribute(updateListenerAttributeReq)
d.logger.Debug("sdk request 'alb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.UpdateListenerAttribute'")
}
d.logger.Logt("已更新 ALB 监听配置", updateListenerAttributeResp)
} else {
// 指定 SNI需部署到扩展域名
// 查询监听证书列表
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
listenerCertificates := make([]aliyunAlb.ListListenerCertificatesResponseBodyCertificates, 0)
listenerCertificates := make([]alialb.ListListenerCertificatesResponseBodyCertificates, 0)
listListenerCertificatesLimit := int32(100)
var listListenerCertificatesToken *string = nil
for {
listListenerCertificatesReq := &aliyunAlb.ListListenerCertificatesRequest{
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
NextToken: listListenerCertificatesToken,
MaxResults: tea.Int32(listListenerCertificatesLimit),
ListenerId: tea.String(cloudListenerId),
CertificateType: tea.String("Server"),
}
listListenerCertificatesResp, err := d.sdkClients.alb.ListListenerCertificates(listListenerCertificatesReq)
listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificates(listListenerCertificatesReq)
d.logger.Debug("sdk request 'alb.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.ListListenerCertificates'")
}
@@ -286,53 +288,65 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
}
}
d.logger.Logt("已查询到 ALB 监听下全部证书", listenerCertificates)
// 遍历查询监听证书,并找出需要解除关联的证书
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail
certificateIsAssociated := false
certificateIdsExpired := make([]string, 0)
certificateIsAlreadyAssociated := false
certificateIdsToDissociate := make([]string, 0)
if len(listenerCertificates) > 0 {
d.logger.Info("found listener certificates to deploy", slog.Any("listenerCertificates", listenerCertificates))
var errs []error
for _, listenerCertificate := range listenerCertificates {
if *listenerCertificate.CertificateId == cloudCertId {
certificateIsAssociated = true
if tea.BoolValue(listenerCertificate.IsDefault) {
continue
}
if *listenerCertificate.IsDefault || !strings.EqualFold(*listenerCertificate.Status, "Associated") {
if !strings.EqualFold(tea.StringValue(listenerCertificate.Status), "Associated") {
continue
}
listenerCertificateId, err := strconv.ParseInt(*listenerCertificate.CertificateId, 10, 64)
// 监听证书 ID 格式:${证书 ID}-${地域}
certificateId := strings.Split(tea.StringValue(listenerCertificate.CertificateId), "-")[0]
if certificateId == cloudCertId {
certificateIsAlreadyAssociated = true
break
}
certificateIdAsInt64, err := strconv.ParseInt(certificateId, 10, 64)
if err != nil {
errs = append(errs, err)
continue
}
getUserCertificateDetailReq := &aliyunCas.GetUserCertificateDetailRequest{
CertId: tea.Int64(listenerCertificateId),
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
CertId: tea.Int64(certificateIdAsInt64),
}
getUserCertificateDetailResp, err := d.sdkClients.cas.GetUserCertificateDetail(getUserCertificateDetailReq)
getUserCertificateDetailResp, err := d.sdkClients.CAS.GetUserCertificateDetail(getUserCertificateDetailReq)
d.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
if err != nil {
if sdkerr, ok := err.(*tea.SDKError); ok {
if tea.IntValue(sdkerr.StatusCode) == 400 && tea.StringValue(sdkerr.Code) == "NotFound" {
continue
}
}
errs = append(errs, xerrors.Wrap(err, "failed to execute sdk request 'cas.GetUserCertificateDetail'"))
continue
}
} else {
certCNMatched := tea.StringValue(getUserCertificateDetailResp.Body.Common) == d.config.Domain
certSANMatched := slices.Contains(strings.Split(tea.StringValue(getUserCertificateDetailResp.Body.Sans), ","), d.config.Domain)
if !certCNMatched && !certSANMatched {
continue
}
certCnMatched := getUserCertificateDetailResp.Body.Common != nil && *getUserCertificateDetailResp.Body.Common == d.config.Domain
certSanMatched := getUserCertificateDetailResp.Body.Sans != nil && slices.Contains(strings.Split(*getUserCertificateDetailResp.Body.Sans, ","), d.config.Domain)
if !certCnMatched && !certSanMatched {
continue
}
certEndDate, _ := time.Parse("2006-01-02", tea.StringValue(getUserCertificateDetailResp.Body.EndDate))
if time.Now().Before(certEndDate) {
continue
}
certEndDate, _ := time.Parse("2006-01-02", *getUserCertificateDetailResp.Body.EndDate)
if time.Now().Before(certEndDate) {
continue
certificateIdsToDissociate = append(certificateIdsToDissociate, certificateId)
}
certificateIdsExpired = append(certificateIdsExpired, *listenerCertificate.CertificateId)
}
if len(errs) > 0 {
@@ -342,43 +356,41 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
// 关联监听和扩展证书
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-associateadditionalcertificateswithlistener
if !certificateIsAssociated {
associateAdditionalCertificatesFromListenerReq := &aliyunAlb.AssociateAdditionalCertificatesWithListenerRequest{
if !certificateIsAlreadyAssociated {
associateAdditionalCertificatesFromListenerReq := &alialb.AssociateAdditionalCertificatesWithListenerRequest{
ListenerId: tea.String(cloudListenerId),
Certificates: []*aliyunAlb.AssociateAdditionalCertificatesWithListenerRequestCertificates{
Certificates: []*alialb.AssociateAdditionalCertificatesWithListenerRequestCertificates{
{
CertificateId: tea.String(cloudCertId),
},
},
}
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.alb.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq)
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq)
d.logger.Debug("sdk request 'alb.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesFromListenerReq), slog.Any("response", associateAdditionalCertificatesFromListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener'")
}
d.logger.Logt("已关联 ALB 监听和扩展证书", associateAdditionalCertificatesFromListenerResp)
}
// 解除关联监听和扩展证书
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-dissociateadditionalcertificatesfromlistener
if len(certificateIdsExpired) > 0 {
dissociateAdditionalCertificates := make([]*aliyunAlb.DissociateAdditionalCertificatesFromListenerRequestCertificates, 0)
for _, certificateId := range certificateIdsExpired {
dissociateAdditionalCertificates = append(dissociateAdditionalCertificates, &aliyunAlb.DissociateAdditionalCertificatesFromListenerRequestCertificates{
if !certificateIsAlreadyAssociated && len(certificateIdsToDissociate) > 0 {
dissociateAdditionalCertificates := make([]*alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates, 0)
for _, certificateId := range certificateIdsToDissociate {
dissociateAdditionalCertificates = append(dissociateAdditionalCertificates, &alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates{
CertificateId: tea.String(certificateId),
})
}
dissociateAdditionalCertificatesFromListenerReq := &aliyunAlb.DissociateAdditionalCertificatesFromListenerRequest{
dissociateAdditionalCertificatesFromListenerReq := &alialb.DissociateAdditionalCertificatesFromListenerRequest{
ListenerId: tea.String(cloudListenerId),
Certificates: dissociateAdditionalCertificates,
}
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.alb.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq)
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq)
d.logger.Debug("sdk request 'alb.DissociateAdditionalCertificatesFromListener'", slog.Any("request", dissociateAdditionalCertificatesFromListenerReq), slog.Any("response", dissociateAdditionalCertificatesFromListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener'")
}
d.logger.Logt("已解除关联 ALB 监听和扩展证书", dissociateAdditionalCertificatesFromListenerResp)
}
}
@@ -386,7 +398,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
}
func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients, error) {
// 接入点一览 https://www.alibabacloud.com/help/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-albEndpoint
// 接入点一览 https://api.aliyun.com/product/Alb
var albEndpoint string
switch region {
case "cn-hangzhou-finance":
@@ -395,17 +407,17 @@ func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients
albEndpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
}
albConfig := &aliyunOpen.Config{
albConfig := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(albEndpoint),
}
albClient, err := aliyunAlb.NewClient(albConfig)
albClient, err := alialb.NewClient(albConfig)
if err != nil {
return nil, err
}
// 接入点一览 https://help.aliyun.com/zh/ssl-certificate/developer-reference/endpoints
// 接入点一览 https://api.aliyun.com/product/cas
var casEndpoint string
if !strings.HasPrefix(region, "cn-") {
casEndpoint = "cas.ap-southeast-1.aliyuncs.com"
@@ -413,19 +425,19 @@ func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients
casEndpoint = "cas.aliyuncs.com"
}
casConfig := &aliyunOpen.Config{
casConfig := &aliopen.Config{
Endpoint: tea.String(casEndpoint),
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
}
casClient, err := aliyunCas.NewClient(casConfig)
casClient, err := alicas.NewClient(casConfig)
if err != nil {
return nil, err
}
return &wSdkClients{
alb: albClient,
cas: casClient,
ALB: albClient,
CAS: casClient,
}, nil
}

View File

@@ -4,16 +4,16 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
"time"
aliyunCas "github.com/alibabacloud-go/cas-20200407/v3/client"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
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/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
)
@@ -34,8 +34,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunCas.Client
logger *slog.Logger
sdkClient *alicas.Client
sslUploader uploader.Uploader
}
@@ -51,21 +51,30 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret, config.Region)
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
AccessKeySecret: config.AccessKeySecret,
Region: config.Region,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -78,18 +87,19 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
contactIds := d.config.ContactIds
if len(contactIds) == 0 {
// 获取联系人列表
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-listcontact
listContactReq := &aliyunCas.ListContactRequest{}
listContactReq := &alicas.ListContactRequest{}
listContactReq.ShowSize = tea.Int32(1)
listContactReq.CurrentPage = tea.Int32(1)
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'")
}
@@ -101,7 +111,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 创建部署任务
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-createdeploymentjob
createDeploymentJobReq := &aliyunCas.CreateDeploymentJobRequest{
createDeploymentJobReq := &alicas.CreateDeploymentJobRequest{
Name: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
JobType: tea.String("user"),
CertIds: tea.String(upres.CertId),
@@ -109,12 +119,11 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
ContactIds: tea.String(strings.Join(contactIds, ",")),
}
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'")
}
d.logger.Logt("已创建部署任务", createDeploymentJobResp)
// 循环获取部署任务详情,等待任务状态变更
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-describedeploymentjob
for {
@@ -122,36 +131,36 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return nil, ctx.Err()
}
describeDeploymentJobReq := &aliyunCas.DescribeDeploymentJobRequest{
describeDeploymentJobReq := &alicas.DescribeDeploymentJobRequest{
JobId: createDeploymentJobResp.Body.JobId,
}
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'")
}
if describeDeploymentJobResp.Body.Status == nil || *describeDeploymentJobResp.Body.Status == "editing" {
return nil, errors.New("部署任务状态异常")
return nil, errors.New("unexpected deployment job status")
}
if *describeDeploymentJobResp.Body.Status == "success" || *describeDeploymentJobResp.Body.Status == "error" {
d.logger.Logt("已获取部署任务详情", describeDeploymentJobResp)
break
}
d.logger.Logt("部署任务未完成 ...")
d.logger.Info("waiting for deployment job completion ...")
time.Sleep(time.Second * 5)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Client, error) {
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alicas.Client, error) {
if region == "" {
region = "cn-hangzhou" // CAS 服务默认区域:华东一杭州
}
// 接入点一览 https://help.aliyun.com/zh/ssl-certificate/developer-reference/endpoints
// 接入点一览 https://api.aliyun.com/product/cas
var endpoint string
switch region {
case "cn-hangzhou":
@@ -160,25 +169,16 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunCas.Cl
endpoint = fmt.Sprintf("cas.%s.aliyuncs.com", region)
}
config := &aliyunOpen.Config{
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(endpoint),
}
client, err := aliyunCas.NewClient(config)
client, err := alicas.NewClient(config)
if err != nil {
return nil, err
}
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

@@ -0,0 +1,72 @@
package aliyuncas
import (
"context"
"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"
)
type DeployerConfig struct {
// 阿里云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 阿里云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 阿里云地域。
Region string `json:"region"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
AccessKeySecret: config.AccessKeySecret,
Region: config.Region,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
d.sslUploader.WithLogger(logger)
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
return &deployer.DeployResult{}, nil
}

View File

@@ -3,16 +3,16 @@
import (
"context"
"fmt"
"log/slog"
"strings"
"time"
aliyunCdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
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"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -26,8 +26,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunCdn.Client
logger *slog.Logger
sdkClient *alicdn.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -44,13 +44,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -60,7 +64,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 设置 CDN 域名域名证书
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
setCdnDomainSSLCertificateReq := &aliyunCdn.SetCdnDomainSSLCertificateRequest{
setCdnDomainSSLCertificateReq := &alicdn.SetCdnDomainSSLCertificateRequest{
DomainName: tea.String(domain),
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
@@ -69,23 +73,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
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'")
}
d.logger.Logt("已设置 CDN 域名证书", setCdnDomainSSLCertificateResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunCdn.Client, error) {
config := &aliyunOpen.Config{
func createSdkClient(accessKeyId, accessKeySecret string) (*alicdn.Client, error) {
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String("cdn.aliyuncs.com"),
}
client, err := aliyunCdn.NewClient(config)
client, err := alicdn.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -4,14 +4,14 @@ import (
"context"
"errors"
"fmt"
"log/slog"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliyunSlb "github.com/alibabacloud-go/slb-20140515/v4/client"
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/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-slb"
)
@@ -38,8 +38,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunSlb.Client
logger *slog.Logger
sdkClient *alislb.Client
sslUploader uploader.Uploader
}
@@ -66,14 +66,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -82,10 +87,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
@@ -112,24 +117,23 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
// 查询负载均衡实例的详细信息
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerattribute
describeLoadBalancerAttributeReq := &aliyunSlb.DescribeLoadBalancerAttributeRequest{
describeLoadBalancerAttributeReq := &alislb.DescribeLoadBalancerAttributeRequest{
RegionId: tea.String(d.config.Region),
LoadBalancerId: tea.String(d.config.LoadbalancerId),
}
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'")
}
d.logger.Logt("已查询到 CLB 负载均衡实例", describeLoadBalancerAttributeResp)
// 查询 HTTPS 监听列表
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerlisteners
listenerPorts := make([]int32, 0)
describeLoadBalancerListenersLimit := int32(100)
var describeLoadBalancerListenersToken *string = nil
for {
describeLoadBalancerListenersReq := &aliyunSlb.DescribeLoadBalancerListenersRequest{
describeLoadBalancerListenersReq := &alislb.DescribeLoadBalancerListenersRequest{
RegionId: tea.String(d.config.Region),
MaxResults: tea.Int32(describeLoadBalancerListenersLimit),
NextToken: describeLoadBalancerListenersToken,
@@ -137,6 +141,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
ListenerProtocol: tea.String("https"),
}
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'")
}
@@ -154,12 +159,11 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
}
}
d.logger.Logt("已查询到 CLB 负载均衡实例下的全部 HTTPS 监听", listenerPorts)
// 遍历更新监听证书
if len(listenerPorts) == 0 {
return errors.New("listener not found")
d.logger.Info("no clb listeners to deploy")
} else {
d.logger.Info("found https listeners to deploy", slog.Any("listenerPorts", listenerPorts))
var errs []error
for _, listenerPort := range listenerPorts {
@@ -195,51 +199,48 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerPort int32, cloudCertId string) error {
// 查询监听配置
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describeloadbalancerhttpslistenerattribute
describeLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.DescribeLoadBalancerHTTPSListenerAttributeRequest{
describeLoadBalancerHTTPSListenerAttributeReq := &alislb.DescribeLoadBalancerHTTPSListenerAttributeRequest{
LoadBalancerId: tea.String(cloudLoadbalancerId),
ListenerPort: tea.Int32(cloudListenerPort),
}
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'")
}
d.logger.Logt("已查询到 CLB HTTPS 监听配置", describeLoadBalancerHTTPSListenerAttributeResp)
if d.config.Domain == "" {
// 未指定 SNI只需部署到监听器
// 修改监听配置
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute
setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{
setLoadBalancerHTTPSListenerAttributeReq := &alislb.SetLoadBalancerHTTPSListenerAttributeRequest{
RegionId: tea.String(d.config.Region),
LoadBalancerId: tea.String(cloudLoadbalancerId),
ListenerPort: tea.Int32(cloudListenerPort),
ServerCertificateId: tea.String(cloudCertId),
}
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'")
}
d.logger.Logt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp)
} else {
// 指定 SNI需部署到扩展域名
// 查询扩展域名
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions
describeDomainExtensionsReq := &aliyunSlb.DescribeDomainExtensionsRequest{
describeDomainExtensionsReq := &alislb.DescribeDomainExtensionsRequest{
RegionId: tea.String(d.config.Region),
LoadBalancerId: tea.String(cloudLoadbalancerId),
ListenerPort: tea.Int32(cloudListenerPort),
}
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'")
}
d.logger.Logt("已查询到 CLB 扩展域名", describeDomainExtensionsResp)
// 遍历修改扩展域名
// REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setdomainextensionattribute
if describeDomainExtensionsResp.Body.DomainExtensions != nil && describeDomainExtensionsResp.Body.DomainExtensions.DomainExtension != nil {
@@ -250,18 +251,17 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
continue
}
setDomainExtensionAttributeReq := &aliyunSlb.SetDomainExtensionAttributeRequest{
setDomainExtensionAttributeReq := &alislb.SetDomainExtensionAttributeRequest{
RegionId: tea.String(d.config.Region),
DomainExtensionId: tea.String(*domainExtension.DomainExtensionId),
ServerCertificateId: tea.String(cloudCertId),
}
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'"))
continue
}
d.logger.Logt("已修改 CLB 扩展域名", setDomainExtensionAttributeResp)
}
if len(errs) > 0 {
@@ -273,8 +273,8 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
return nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-endpoint
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alislb.Client, error) {
// 接入点一览 https://api.aliyun.com/product/Slb
var endpoint string
switch region {
case
@@ -287,13 +287,13 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunSlb.Cl
endpoint = fmt.Sprintf("slb.%s.aliyuncs.com", region)
}
config := &aliyunOpen.Config{
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(endpoint),
}
client, err := aliyunSlb.NewClient(config)
client, err := alislb.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -46,7 +46,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_ALIYUNCLB_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNCLB_LOADBALANCERID="your-clb-instance-id" \
--CERTIMATE_DEPLOYER_ALIYUNCLB_LISTENERPORT=443 \
--CERTIMATE_DEPLOYER_ALIYUNCLB_DOMAIN="your-alb-sni-domain"
--CERTIMATE_DEPLOYER_ALIYUNCLB_DOMAIN="your-clb-sni-domain"
*/
func TestDeploy(t *testing.T) {
flag.Parse()

View File

@@ -3,16 +3,16 @@
import (
"context"
"fmt"
"log/slog"
"strings"
"time"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliyunDcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
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"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -26,8 +26,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunDcdn.Client
logger *slog.Logger
sdkClient *alidcdn.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -44,13 +44,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -60,7 +64,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 配置域名证书
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
setDcdnDomainSSLCertificateReq := &aliyunDcdn.SetDcdnDomainSSLCertificateRequest{
setDcdnDomainSSLCertificateReq := &alidcdn.SetDcdnDomainSSLCertificateRequest{
DomainName: tea.String(domain),
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
@@ -69,23 +73,22 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
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'")
}
d.logger.Logt("已配置 DCDN 域名证书", setDcdnDomainSSLCertificateResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*aliyunDcdn.Client, error) {
config := &aliyunOpen.Config{
func createSdkClient(accessKeyId, accessKeySecret string) (*alidcdn.Client, error) {
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String("dcdn.aliyuncs.com"),
}
client, err := aliyunDcdn.NewClient(config)
client, err := alidcdn.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -4,16 +4,16 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strconv"
"strings"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliyunEsa "github.com/alibabacloud-go/esa-20240910/v2/client"
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/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
)
@@ -31,8 +31,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunEsa.Client
logger *slog.Logger
sdkClient *aliesa.Client
sslUploader uploader.Uploader
}
@@ -55,14 +55,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -75,37 +80,36 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 配置站点证书
// REF: https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-setcertificate
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
setCertificateReq := &aliyunEsa.SetCertificateRequest{
setCertificateReq := &aliesa.SetCertificateRequest{
SiteId: tea.Int64(d.config.SiteId),
Type: tea.String("cas"),
CasId: tea.Int64(certId),
}
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'")
}
d.logger.Logt("已配置站点证书", setCertificateResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunEsa.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/edge-security-acceleration/esa/api-esa-2024-09-10-endpoint
config := &aliyunOpen.Config{
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliesa.Client, error) {
// 接入点一览 https://api.aliyun.com/product/ESA
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", region)),
}
client, err := aliyunEsa.NewClient(config)
client, err := aliesa.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -28,7 +28,7 @@ func init() {
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.Int64Var(&fSiteId, argsPrefix+"SITEID", "", "")
flag.Int64Var(&fSiteId, argsPrefix+"SITEID", 0, "")
}
/*
@@ -39,7 +39,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_ALIYUNESA_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_ALIYUNESA_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_ALIYUNESA_ACCESSKEYSECRET="your-access-key-secret" \
--CERTIMATE_DEPLOYER_ALIYUNOSS_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNESA_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNESA_SITEID="your-esa-site-id"
*/
func TestDeploy(t *testing.T) {

View File

@@ -0,0 +1,185 @@
package aliyunfc
import (
"context"
"fmt"
"log/slog"
"time"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
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"
)
type DeployerConfig struct {
// 阿里云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 阿里云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 阿里云地域。
Region string `json:"region"`
// 服务版本。
ServiceVersion string `json:"serviceVersion"`
// 自定义域名(支持泛域名)。
Domain string `json:"domain"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClients *wSdkClients
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
type wSdkClients struct {
FC2 *alifc2.Client
FC3 *alifc3.Client
}
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, xerrors.Wrap(err, "failed to create sdk clients")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sdkClients: clients,
}, 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.ServiceVersion {
case "3", "3.0":
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 {
return nil, err
}
default:
return nil, xerrors.Errorf("unsupported service version: %s", d.config.ServiceVersion)
}
return &deployer.DeployResult{}, nil
}
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'")
}
// 更新自定义域名
// REF: https://help.aliyun.com/zh/functioncompute/fc-3-0/developer-reference/api-fc-2023-03-30-updatecustomdomain
updateCustomDomainReq := &alifc3.UpdateCustomDomainRequest{
Body: &alifc3.UpdateCustomDomainInput{
CertConfig: &alifc3.CertConfig{
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
Certificate: tea.String(certPem),
PrivateKey: tea.String(privkeyPem),
},
Protocol: getCustomDomainResp.Body.Protocol,
TlsConfig: getCustomDomainResp.Body.TlsConfig,
},
}
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 nil
}
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'")
}
// 更新自定义域名
// REF: https://help.aliyun.com/zh/functioncompute/fc-2-0/developer-reference/api-fc-open-2021-04-06-updatecustomdomain
updateCustomDomainReq := &alifc2.UpdateCustomDomainRequest{
CertConfig: &alifc2.CertConfig{
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
Certificate: tea.String(certPem),
PrivateKey: tea.String(privkeyPem),
},
Protocol: getCustomDomainResp.Body.Protocol,
TlsConfig: getCustomDomainResp.Body.TlsConfig,
}
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 nil
}
func createSdkClients(accessKeyId, accessKeySecret, region string) (*wSdkClients, error) {
// 接入点一览 https://api.aliyun.com/product/FC-Open
var fc2Endpoint string
switch region {
case "cn-hangzhou-finance":
fc2Endpoint = fmt.Sprintf("%s.fc.aliyuncs.com", region)
default:
fc2Endpoint = fmt.Sprintf("fc.%s.aliyuncs.com", region)
}
fc2Config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(fc2Endpoint),
}
fc2Client, err := alifc2.NewClient(fc2Config)
if err != nil {
return nil, err
}
// 接入点一览 https://api.aliyun.com/product/FC-Open
fc3Endpoint := fmt.Sprintf("fcv3.%s.aliyuncs.com", region)
fc3Config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(fc3Endpoint),
}
fc3Client, err := alifc3.NewClient(fc3Config)
if err != nil {
return nil, err
}
return &wSdkClients{
FC2: fc2Client,
FC3: fc3Client,
}, nil
}

View File

@@ -0,0 +1,80 @@
package aliyunfc_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-fc"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fAccessKeySecret string
fRegion string
fDomain string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNFC_"
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_fc_test.go -args \
--CERTIMATE_DEPLOYER_ALIYUNFC_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_ALIYUNFC_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_ALIYUNFC_ACCESSKEYSECRET="your-access-key-secret" \
--CERTIMATE_DEPLOYER_ALIYUNFC_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNFC_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

@@ -3,16 +3,16 @@
import (
"context"
"fmt"
"log/slog"
"strings"
"time"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliyunLive "github.com/alibabacloud-go/live-20161101/client"
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"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -28,8 +28,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunLive.Client
logger *slog.Logger
sdkClient *alilive.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -46,13 +46,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -62,7 +66,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 设置域名证书
// REF: https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-setlivedomaincertificate
setLiveDomainSSLCertificateReq := &aliyunLive.SetLiveDomainCertificateRequest{
setLiveDomainSSLCertificateReq := &alilive.SetLiveDomainCertificateRequest{
DomainName: tea.String(domain),
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
@@ -71,17 +75,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
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'")
}
d.logger.Logt("已设置域名证书", setLiveDomainSSLCertificateResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunLive.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/live/developer-reference/api-live-2016-11-01-endpoint
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alilive.Client, error) {
// 接入点一览 https://api.aliyun.com/product/live
var endpoint string
switch region {
case
@@ -97,13 +100,13 @@ func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunLive.C
endpoint = fmt.Sprintf("live.%s.aliyuncs.com", region)
}
config := &aliyunOpen.Config{
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(endpoint),
}
client, err := aliyunLive.NewClient(config)
client, err := alilive.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -4,15 +4,15 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliyunNlb "github.com/alibabacloud-go/nlb-20220430/v2/client"
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/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
)
@@ -36,8 +36,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunNlb.Client
logger *slog.Logger
sdkClient *alinlb.Client
sslUploader uploader.Uploader
}
@@ -60,14 +60,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -76,10 +81,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
@@ -106,29 +111,29 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
// 查询负载均衡实例的详细信息
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getloadbalancerattribute
getLoadBalancerAttributeReq := &aliyunNlb.GetLoadBalancerAttributeRequest{
getLoadBalancerAttributeReq := &alinlb.GetLoadBalancerAttributeRequest{
LoadBalancerId: tea.String(d.config.LoadbalancerId),
}
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'")
}
d.logger.Logt("已查询到 NLB 负载均衡实例", getLoadBalancerAttributeResp)
// 查询 TCPSSL 监听列表
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-listlisteners
listenerIds := make([]string, 0)
listListenersLimit := int32(100)
var listListenersToken *string = nil
for {
listListenersReq := &aliyunNlb.ListListenersRequest{
listListenersReq := &alinlb.ListListenersRequest{
MaxResults: tea.Int32(listListenersLimit),
NextToken: listListenersToken,
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
ListenerProtocol: tea.String("TCPSSL"),
}
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'")
}
@@ -146,12 +151,11 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
}
}
d.logger.Logt("已查询到 NLB 负载均衡实例下的全部 TCPSSL 监听", listenerIds)
// 遍历更新监听证书
if len(listenerIds) == 0 {
return errors.New("listener not found")
d.logger.Info("no nlb listeners to deploy")
} else {
d.logger.Info("found tcpssl listeners to deploy", slog.Any("listenerIds", listenerIds))
var errs []error
for _, listenerId := range listenerIds {
@@ -184,47 +188,45 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
// 查询监听的属性
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-getlistenerattribute
getListenerAttributeReq := &aliyunNlb.GetListenerAttributeRequest{
getListenerAttributeReq := &alinlb.GetListenerAttributeRequest{
ListenerId: tea.String(cloudListenerId),
}
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'")
}
d.logger.Logt("已查询到 NLB 监听配置", getListenerAttributeResp)
// 修改监听的属性
// REF: https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-updatelistenerattribute
updateListenerAttributeReq := &aliyunNlb.UpdateListenerAttributeRequest{
updateListenerAttributeReq := &alinlb.UpdateListenerAttributeRequest{
ListenerId: tea.String(cloudListenerId),
CertificateIds: []*string{tea.String(cloudCertId)},
}
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'")
}
d.logger.Logt("已更新 NLB 监听配置", updateListenerAttributeResp)
return nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunNlb.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/slb/network-load-balancer/developer-reference/api-nlb-2022-04-30-endpoint
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alinlb.Client, error) {
// 接入点一览 https://api.aliyun.com/product/Nlb
var endpoint string
switch region {
default:
endpoint = fmt.Sprintf("nlb.%s.aliyuncs.com", region)
}
config := &aliyunOpen.Config{
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(endpoint),
}
client, err := aliyunNlb.NewClient(config)
client, err := alinlb.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -4,12 +4,12 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -27,7 +27,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *oss.Client
}
@@ -45,13 +45,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -65,14 +69,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 为存储空间绑定自定义域名
// REF: https://help.aliyun.com/zh/oss/developer-reference/putcname
err := d.sdkClient.PutBucketCnameWithCertificate(d.config.Bucket, oss.PutBucketCname{
putBucketCnameWithCertificateReq := oss.PutBucketCname{
Cname: d.config.Domain,
CertificateConfiguration: &oss.CertificateConfiguration{
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'")
}
@@ -81,7 +87,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*oss.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/oss/user-guide/regions-and-endpoints
// 接入点一览 https://api.aliyun.com/product/Oss
var endpoint string
switch region {
case "":

View File

@@ -3,15 +3,15 @@
import (
"context"
"fmt"
"log/slog"
"time"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
aliyunVod "github.com/alibabacloud-go/vod-20170321/v4/client"
alivod "github.com/alibabacloud-go/vod-20170321/v4/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -27,8 +27,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunVod.Client
logger *slog.Logger
sdkClient *alivod.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -45,20 +45,24 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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) {
// 设置域名证书
// REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate
setVodDomainSSLCertificateReq := &aliyunVod.SetVodDomainSSLCertificateRequest{
setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{
DomainName: tea.String(d.config.Domain),
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertType: tea.String("upload"),
@@ -67,26 +71,25 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
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'")
} else {
d.logger.Logt("已设置域名证书", setVodDomainSSLCertificateResp)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunVod.Client, error) {
// 接入点一览 https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-endpoint
func createSdkClient(accessKeyId, accessKeySecret, region string) (*alivod.Client, error) {
// 接入点一览 https://api.aliyun.com/product/vod
endpoint := fmt.Sprintf("vod.%s.aliyuncs.com", region)
config := &aliyunOpen.Config{
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(endpoint),
}
client, err := aliyunVod.NewClient(config)
client, err := alivod.NewClient(config)
if err != nil {
return nil, err
}

View File

@@ -4,17 +4,18 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
aliyunOpen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
aliyunWaf "github.com/alibabacloud-go/waf-openapi-20211001/v5/client"
aliwaf "github.com/alibabacloud-go/waf-openapi-20211001/v5/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"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"
)
type DeployerConfig struct {
@@ -24,14 +25,18 @@ type DeployerConfig struct {
AccessKeySecret string `json:"accessKeySecret"`
// 阿里云地域。
Region string `json:"region"`
// 阿里云 WAF 实例 ID
// 服务版本
ServiceVersion string `json:"serviceVersion"`
// WAF 实例 ID。
InstanceId string `json:"instanceId"`
// 接入域名(支持泛域名)。
Domain string `json:"domain,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *aliyunWaf.Client
logger *slog.Logger
sdkClient *aliwaf.Client
sslUploader uploader.Uploader
}
@@ -54,14 +59,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -70,55 +80,106 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return nil, errors.New("config `instanceId` is required")
}
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
}
switch d.config.ServiceVersion {
case "3", "3.0":
if err := d.deployToWAF3(ctx, certPem, privkeyPem); err != nil {
return nil, err
}
d.logger.Logt("certificate file uploaded", upres)
// 查询默认 SSL/TLS 设置
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedefaulthttps
describeDefaultHttpsReq := &aliyunWaf.DescribeDefaultHttpsRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
default:
return nil, xerrors.Errorf("unsupported service version: %s", d.config.ServiceVersion)
}
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttps(describeDefaultHttpsReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDefaultHttps'")
}
d.logger.Logt("已查询到默认 SSL/TLS 设置", describeDefaultHttpsResp)
// 修改默认 SSL/TLS 设置
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydefaulthttps
modifyDefaultHttpsReq := &aliyunWaf.ModifyDefaultHttpsRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
CertId: tea.String(upres.CertId),
TLSVersion: describeDefaultHttpsResp.Body.DefaultHttps.TLSVersion,
EnableTLSv3: describeDefaultHttpsResp.Body.DefaultHttps.EnableTLSv3,
}
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttps(modifyDefaultHttpsReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDefaultHttps'")
}
d.logger.Logt("已修改默认 SSL/TLS 设置", modifyDefaultHttpsResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliyunWaf.Client, error) {
// 接入点一览https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-endpoint
config := &aliyunOpen.Config{
func (d *DeployerProvider) deployToWAF3(ctx context.Context, certPem string, privkeyPem string) error {
// 上传证书到 CAS
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
if d.config.Domain == "" {
// 未指定接入域名,只需替换默认证书即可
// 查询默认 SSL/TLS 设置
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedefaulthttps
describeDefaultHttpsReq := &aliwaf.DescribeDefaultHttpsRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
}
describeDefaultHttpsResp, err := d.sdkClient.DescribeDefaultHttps(describeDefaultHttpsReq)
d.logger.Debug("sdk request 'waf.DescribeDefaultHttps'", slog.Any("request", describeDefaultHttpsReq), slog.Any("response", describeDefaultHttpsResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDefaultHttps'")
}
// 修改默认 SSL/TLS 设置
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydefaulthttps
modifyDefaultHttpsReq := &aliwaf.ModifyDefaultHttpsRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
CertId: tea.String(upres.CertId),
TLSVersion: tea.String("tlsv1"),
EnableTLSv3: tea.Bool(false),
}
if describeDefaultHttpsResp.Body != nil && describeDefaultHttpsResp.Body.DefaultHttps != nil {
modifyDefaultHttpsReq.TLSVersion = describeDefaultHttpsResp.Body.DefaultHttps.TLSVersion
modifyDefaultHttpsReq.EnableTLSv3 = describeDefaultHttpsResp.Body.DefaultHttps.EnableTLSv3
}
modifyDefaultHttpsResp, err := d.sdkClient.ModifyDefaultHttps(modifyDefaultHttpsReq)
d.logger.Debug("sdk request 'waf.ModifyDefaultHttps'", slog.Any("request", modifyDefaultHttpsReq), slog.Any("response", modifyDefaultHttpsResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDefaultHttps'")
}
} else {
// 指定接入域名
// 查询 CNAME 接入详情
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-describedomaindetail
describeDomainDetailReq := &aliwaf.DescribeDomainDetailRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
Domain: tea.String(d.config.Domain),
}
describeDomainDetailResp, err := d.sdkClient.DescribeDomainDetail(describeDomainDetailReq)
d.logger.Debug("sdk request 'waf.DescribeDomainDetail'", slog.Any("request", describeDomainDetailReq), slog.Any("response", describeDomainDetailResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.DescribeDomainDetail'")
}
// 修改 CNAME 接入资源
// REF: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-modifydomain
modifyDomainReq := &aliwaf.ModifyDomainRequest{
InstanceId: tea.String(d.config.InstanceId),
RegionId: tea.String(d.config.Region),
Domain: tea.String(d.config.Domain),
Listen: &aliwaf.ModifyDomainRequestListen{CertId: tea.String(upres.ExtendedData["certIdentifier"].(string))},
Redirect: &aliwaf.ModifyDomainRequestRedirect{Loadbalance: tea.String("iphash")},
}
modifyDomainReq = assign(modifyDomainReq, describeDomainDetailResp.Body)
modifyDomainResp, err := d.sdkClient.ModifyDomain(modifyDomainReq)
d.logger.Debug("sdk request 'waf.ModifyDomain'", slog.Any("request", modifyDomainReq), slog.Any("response", modifyDomainResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.ModifyDomain'")
}
}
return nil
}
func createSdkClient(accessKeyId, accessKeySecret, region string) (*aliwaf.Client, error) {
// 接入点一览https://api.aliyun.com/product/waf-openapi
config := &aliopen.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
Endpoint: tea.String(fmt.Sprintf("wafopenapi.%s.aliyuncs.com", region)),
}
client, err := aliyunWaf.NewClient(config)
client, err := aliwaf.NewClient(config)
if err != nil {
return nil, err
}
@@ -146,3 +207,166 @@ func createSslUploader(accessKeyId, accessKeySecret, region string) (uploader.Up
})
return uploader, err
}
func assign(source *aliwaf.ModifyDomainRequest, target *aliwaf.DescribeDomainDetailResponseBody) *aliwaf.ModifyDomainRequest {
// `ModifyDomain` 中不传的字段表示使用默认值、而非保留原值,
// 因此这里需要把原配置中的参数重新赋值回去。
if target == nil {
return source
}
if target.Listen != nil {
if source.Listen == nil {
source.Listen = &aliwaf.ModifyDomainRequestListen{}
}
if target.Listen.CipherSuite != nil {
source.Listen.CipherSuite = tea.Int32(int32(*target.Listen.CipherSuite))
}
if target.Listen.CustomCiphers != nil {
source.Listen.CustomCiphers = target.Listen.CustomCiphers
}
if target.Listen.EnableTLSv3 != nil {
source.Listen.EnableTLSv3 = target.Listen.EnableTLSv3
}
if target.Listen.ExclusiveIp != nil {
source.Listen.ExclusiveIp = target.Listen.ExclusiveIp
}
if target.Listen.FocusHttps != nil {
source.Listen.FocusHttps = target.Listen.FocusHttps
}
if target.Listen.Http2Enabled != nil {
source.Listen.Http2Enabled = target.Listen.Http2Enabled
}
if target.Listen.HttpPorts != nil {
source.Listen.HttpPorts = sliceutil.Map(target.Listen.HttpPorts, func(v *int64) *int32 {
if v == nil {
return nil
}
return tea.Int32(int32(*v))
})
}
if target.Listen.HttpsPorts != nil {
source.Listen.HttpsPorts = sliceutil.Map(target.Listen.HttpsPorts, func(v *int64) *int32 {
if v == nil {
return nil
}
return tea.Int32(int32(*v))
})
}
if target.Listen.IPv6Enabled != nil {
source.Listen.IPv6Enabled = target.Listen.IPv6Enabled
}
if target.Listen.ProtectionResource != nil {
source.Listen.ProtectionResource = target.Listen.ProtectionResource
}
if target.Listen.TLSVersion != nil {
source.Listen.TLSVersion = target.Listen.TLSVersion
}
if target.Listen.XffHeaderMode != nil {
source.Listen.XffHeaderMode = tea.Int32(int32(*target.Listen.XffHeaderMode))
}
if target.Listen.XffHeaders != nil {
source.Listen.XffHeaders = target.Listen.XffHeaders
}
}
if target.Redirect != nil {
if source.Redirect == nil {
source.Redirect = &aliwaf.ModifyDomainRequestRedirect{}
}
if target.Redirect.Backends != nil {
source.Redirect.Backends = sliceutil.Map(target.Redirect.Backends, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectBackends) *string {
if v == nil {
return nil
}
return v.Backend
})
}
if target.Redirect.BackupBackends != nil {
source.Redirect.BackupBackends = sliceutil.Map(target.Redirect.BackupBackends, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectBackupBackends) *string {
if v == nil {
return nil
}
return v.Backend
})
}
if target.Redirect.ConnectTimeout != nil {
source.Redirect.ConnectTimeout = target.Redirect.ConnectTimeout
}
if target.Redirect.FocusHttpBackend != nil {
source.Redirect.FocusHttpBackend = target.Redirect.FocusHttpBackend
}
if target.Redirect.Keepalive != nil {
source.Redirect.Keepalive = target.Redirect.Keepalive
}
if target.Redirect.KeepaliveRequests != nil {
source.Redirect.KeepaliveRequests = target.Redirect.KeepaliveRequests
}
if target.Redirect.KeepaliveTimeout != nil {
source.Redirect.KeepaliveTimeout = target.Redirect.KeepaliveTimeout
}
if target.Redirect.Loadbalance != nil {
source.Redirect.Loadbalance = target.Redirect.Loadbalance
}
if target.Redirect.ReadTimeout != nil {
source.Redirect.ReadTimeout = target.Redirect.ReadTimeout
}
if target.Redirect.RequestHeaders != nil {
source.Redirect.RequestHeaders = sliceutil.Map(target.Redirect.RequestHeaders, func(v *aliwaf.DescribeDomainDetailResponseBodyRedirectRequestHeaders) *aliwaf.ModifyDomainRequestRedirectRequestHeaders {
if v == nil {
return nil
}
return &aliwaf.ModifyDomainRequestRedirectRequestHeaders{
Key: v.Key,
Value: v.Value,
}
})
}
if target.Redirect.Retry != nil {
source.Redirect.Retry = target.Redirect.Retry
}
if target.Redirect.SniEnabled != nil {
source.Redirect.SniEnabled = target.Redirect.SniEnabled
}
if target.Redirect.SniHost != nil {
source.Redirect.SniHost = target.Redirect.SniHost
}
if target.Redirect.WriteTimeout != nil {
source.Redirect.WriteTimeout = target.Redirect.WriteTimeout
}
if target.Redirect.XffProto != nil {
source.Redirect.XffProto = target.Redirect.XffProto
}
}
return source
}

View File

@@ -39,7 +39,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_ALIYUNWAF_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_ALIYUNWAF_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_ALIYUNWAF_ACCESSKEYSECRET="your-access-key-secret" \
--CERTIMATE_DEPLOYER_ALIYUNOSS_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNWAF_REGION="cn-hangzhou" \
--CERTIMATE_DEPLOYER_ALIYUNWAF_INSTANCEID="your-waf-instance-id"
*/
func TestDeploy(t *testing.T) {

View File

@@ -0,0 +1,72 @@
package awsacm
import (
"context"
"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/aws-acm"
)
type DeployerConfig struct {
// AWS AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// AWS SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// AWS 区域。
Region string `json:"region"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
d.sslUploader.WithLogger(logger)
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 ACM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
return &deployer.DeployResult{}, nil
}

View File

@@ -3,16 +3,16 @@
import (
"context"
"errors"
"log/slog"
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"
awsCf "github.com/aws/aws-sdk-go-v2/service/cloudfront"
awsCfTypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
awscfg "github.com/aws/aws-sdk-go-v2/config"
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/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aws-acm"
)
@@ -30,8 +30,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *awsCf.Client
logger *slog.Logger
sdkClient *cloudfront.Client
sslUploader uploader.Uploader
}
@@ -58,14 +58,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -78,53 +83,51 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 获取分配配置
// REF: https://docs.aws.amazon.com/en_us/cloudfront/latest/APIReference/API_GetDistributionConfig.html
getDistributionConfigReq := &awsCf.GetDistributionConfigInput{
getDistributionConfigReq := &cloudfront.GetDistributionConfigInput{
Id: aws.String(d.config.DistributionId),
}
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'")
}
d.logger.Logt("已获取分配配置", getDistributionConfigResp)
// 更新分配配置
// REF: https://docs.aws.amazon.com/zh_cn/cloudfront/latest/APIReference/API_UpdateDistribution.html
updateDistributionReq := &awsCf.UpdateDistributionInput{
updateDistributionReq := &cloudfront.UpdateDistributionInput{
Id: aws.String(d.config.DistributionId),
DistributionConfig: getDistributionConfigResp.DistributionConfig,
IfMatch: getDistributionConfigResp.ETag,
}
if updateDistributionReq.DistributionConfig.ViewerCertificate == nil {
updateDistributionReq.DistributionConfig.ViewerCertificate = &awsCfTypes.ViewerCertificate{}
updateDistributionReq.DistributionConfig.ViewerCertificate = &types.ViewerCertificate{}
}
updateDistributionReq.DistributionConfig.ViewerCertificate.CloudFrontDefaultCertificate = aws.Bool(false)
updateDistributionReq.DistributionConfig.ViewerCertificate.ACMCertificateArn = aws.String(upres.CertId)
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'")
}
d.logger.Logt("已更新分配配置", updateDistributionResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*awsCf.Client, error) {
cfg, err := awsCfg.LoadDefaultConfig(context.TODO())
func createSdkClient(accessKeyId, secretAccessKey, region string) (*cloudfront.Client, error) {
cfg, err := awscfg.LoadDefaultConfig(context.TODO())
if err != nil {
return nil, err
}
client := awsCf.NewFromConfig(cfg, func(o *awsCf.Options) {
client := cloudfront.NewFromConfig(cfg, func(o *cloudfront.Options) {
o.Region = region
o.Credentials = aws.NewCredentialsCache(awsCred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, ""))
})
return client, nil
}

View File

@@ -0,0 +1,78 @@
package azurekeyvault
import (
"context"
"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/azure-keyvault"
)
type DeployerConfig struct {
// Azure TenantId。
TenantId string `json:"tenantId"`
// Azure ClientId。
ClientId string `json:"clientId"`
// Azure ClientSecret。
ClientSecret string `json:"clientSecret"`
// Azure 主权云环境。
CloudName string `json:"cloudName,omitempty"`
// Key Vault 名称。
KeyVaultName string `json:"keyvaultName"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
TenantId: config.TenantId,
ClientId: config.ClientId,
ClientSecret: config.ClientSecret,
CloudName: config.CloudName,
KeyVaultName: config.KeyVaultName,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
d.sslUploader.WithLogger(logger)
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 KeyVault
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
return &deployer.DeployResult{}, nil
}

View File

@@ -0,0 +1,332 @@
package baiducloudappblb
import (
"context"
"errors"
"fmt"
"log/slog"
"strconv"
"strings"
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"
)
type DeployerConfig struct {
// 百度智能云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 百度智能云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 百度智能云区域。
Region string `json:"region"`
// 部署资源类型。
ResourceType ResourceType `json:"resourceType"`
// 负载均衡实例 ID。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时必填。
LoadbalancerId string `json:"loadbalancerId,omitempty"`
// 负载均衡监听端口。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
ListenerPort int32 `json:"listenerPort,omitempty"`
// SNI 域名(支持泛域名)。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。
Domain string `json:"domain,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *bceappblb.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.SecretAccessKey, config.Region)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
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
}
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, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
return nil, err
}
case RESOURCE_TYPE_LISTENER:
if err := d.deployToListener(ctx, upres.CertId); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
}
return &deployer.DeployResult{}, nil
}
func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
// 查询 BLB 实例详情
// REF: https://cloud.baidu.com/doc/BLB/s/6jwvxnyhi#describeloadbalancerdetail%E6%9F%A5%E8%AF%A2blb%E5%AE%9E%E4%BE%8B%E8%AF%A6%E6%83%85
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'")
}
// 获取全部 HTTPS/SSL 监听端口
listeners := make([]struct {
Type string
Port int32
}, 0)
for _, listener := range describeLoadBalancerDetailResp.Listener {
if listener.Type == "HTTPS" || listener.Type == "SSL" {
listenerPort, err := strconv.Atoi(listener.Port)
if err != nil {
continue
}
listeners = append(listeners, struct {
Type string
Port int32
}{
Type: listener.Type,
Port: int32(listenerPort),
})
}
}
// 遍历更新监听证书
if len(listeners) == 0 {
d.logger.Info("no blb listeners to deploy")
} else {
d.logger.Info("found https/ssl listeners to deploy", slog.Any("listeners", listeners))
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)
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
if d.config.ListenerPort == 0 {
return errors.New("config `listenerPort` is required")
}
// 查询监听
// REF: https://cloud.baidu.com/doc/BLB/s/ujwvxnyux#describeappalllisteners%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E7%9B%91%E5%90%AC
describeAppAllListenersRequest := &bceappblb.DescribeAppListenerArgs{
ListenerPort: uint16(d.config.ListenerPort),
}
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'")
}
// 获取全部 HTTPS/SSL 监听端口
listeners := make([]struct {
Type string
Port int32
}, 0)
for _, listener := range describeAppAllListenersResp.ListenerList {
if listener.ListenerType == "HTTPS" || listener.ListenerType == "SSL" {
listeners = append(listeners, struct {
Type string
Port int32
}{
Type: listener.ListenerType,
Port: int32(listener.ListenerPort),
})
}
}
// 遍历更新监听证书
if len(listeners) == 0 {
d.logger.Info("no blb listeners to deploy")
} else {
d.logger.Info("found https/ssl listeners to deploy", slog.Any("listeners", listeners))
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)
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerType string, cloudListenerPort int32, cloudCertId string) error {
switch strings.ToUpper(cloudListenerType) {
case "HTTPS":
return d.updateHttpsListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
case "SSL":
return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
default:
return fmt.Errorf("unsupported listener type: %s", cloudListenerType)
}
}
func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudHttpsListenerPort int32, cloudCertId string) error {
// 查询 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/ujwvxnyux#describeapphttpslisteners%E6%9F%A5%E8%AF%A2https%E7%9B%91%E5%90%AC%E5%99%A8
describeAppHTTPSListenersReq := &bceappblb.DescribeAppListenerArgs{
ListenerPort: uint16(cloudHttpsListenerPort),
MaxKeys: 1,
}
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'")
} else if len(describeAppHTTPSListenersResp.ListenerList) == 0 {
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
}
if d.config.Domain == "" {
// 未指定 SNI只需部署到监听器
// 更新 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/ujwvxnyux#updateapphttpslistener%E6%9B%B4%E6%96%B0https%E7%9B%91%E5%90%AC%E5%99%A8
updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
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'")
}
} else {
// 指定 SNI需部署到扩展域名
// 更新 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#updatehttpslistener%E6%9B%B4%E6%96%B0https%E7%9B%91%E5%90%AC%E5%99%A8
updateAppHTTPSListenerReq := &bceappblb.UpdateAppHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
AdditionalCertDomains: sliceutil.Map(describeAppHTTPSListenersResp.ListenerList[0].AdditionalCertDomains, func(domain bceappblb.AdditionalCertDomainsModel) bceappblb.AdditionalCertDomainsModel {
if domain.Host == d.config.Domain {
return bceappblb.AdditionalCertDomainsModel{
Host: domain.Host,
CertId: cloudCertId,
}
}
return bceappblb.AdditionalCertDomainsModel{
Host: domain.Host,
CertId: domain.CertId,
}
}),
}
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 nil
}
func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudHttpsListenerPort int32, cloudCertId string) error {
// 更新 SSL 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/ujwvxnyux#updateappssllistener%E6%9B%B4%E6%96%B0ssl%E7%9B%91%E5%90%AC%E5%99%A8
updateAppSSLListenerReq := &bceappblb.UpdateAppSSLListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
CertIds: []string{cloudCertId},
}
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 nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*bceappblb.Client, error) {
endpoint := ""
if region != "" {
endpoint = fmt.Sprintf("blb.%s.baidubce.com", region)
}
client, err := bceappblb.NewClient(accessKeyId, secretAccessKey, endpoint)
if err != nil {
return nil, err
}
return client, nil
}
func generateClientToken() string {
return strings.ReplaceAll(uuid.New().String(), "-", "")
}

View File

@@ -0,0 +1,86 @@
package baiducloudappblb_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-appblb"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fLoadbalancerId string
fDomain string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}
/*
Shell command to run this test:
go test -v ./baiducloud_appblb_test.go -args \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_SECRETACCESSKEY="your-secret-access-key" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_REGION="bj" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_LOADBALANCERID="your-blb-loadbalancer-id" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDAPPBLB_DOMAIN="your-blb-sni-domain"
*/
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("SECRETACCESSKEY: %v", fSecretAccessKey),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
ResourceType: provider.RESOURCE_TYPE_LOADBALANCER,
Region: fRegion,
LoadbalancerId: fLoadbalancerId,
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 baiducloudappblb
type ResourceType string
const (
// 资源类型:部署到指定负载均衡器。
RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
// 资源类型:部署到指定监听器。
RESOURCE_TYPE_LISTENER = ResourceType("listener")
)

View File

@@ -0,0 +1,332 @@
package baiducloudblb
import (
"context"
"errors"
"fmt"
"log/slog"
"strconv"
"strings"
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"
)
type DeployerConfig struct {
// 百度智能云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 百度智能云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 百度智能云区域。
Region string `json:"region"`
// 部署资源类型。
ResourceType ResourceType `json:"resourceType"`
// 负载均衡实例 ID。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时必填。
LoadbalancerId string `json:"loadbalancerId,omitempty"`
// 负载均衡监听端口。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
ListenerPort int32 `json:"listenerPort,omitempty"`
// SNI 域名(支持泛域名)。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。
Domain string `json:"domain,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *bceblb.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.SecretAccessKey, config.Region)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
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
}
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, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
return nil, err
}
case RESOURCE_TYPE_LISTENER:
if err := d.deployToListener(ctx, upres.CertId); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported resource type: %s", d.config.ResourceType)
}
return &deployer.DeployResult{}, nil
}
func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
// 查询 BLB 实例详情
// REF: https://cloud.baidu.com/doc/BLB/s/njwvxnv79#describeloadbalancerdetail%E6%9F%A5%E8%AF%A2blb%E5%AE%9E%E4%BE%8B%E8%AF%A6%E6%83%85
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'")
}
// 获取全部 HTTPS/SSL 监听端口
listeners := make([]struct {
Type string
Port int32
}, 0)
for _, listener := range describeLoadBalancerDetailResp.Listener {
if listener.Type == "HTTPS" || listener.Type == "SSL" {
listenerPort, err := strconv.Atoi(listener.Port)
if err != nil {
continue
}
listeners = append(listeners, struct {
Type string
Port int32
}{
Type: listener.Type,
Port: int32(listenerPort),
})
}
}
// 遍历更新监听证书
if len(listeners) == 0 {
d.logger.Info("no blb listeners to deploy")
} else {
d.logger.Info("found https/ssl listeners to deploy", slog.Any("listeners", listeners))
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)
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
if d.config.ListenerPort == 0 {
return errors.New("config `listenerPort` is required")
}
// 查询监听
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#describealllisteners%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E7%9B%91%E5%90%AC
describeAllListenersRequest := &bceblb.DescribeListenerArgs{
ListenerPort: uint16(d.config.ListenerPort),
}
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'")
}
// 获取全部 HTTPS/SSL 监听端口
listeners := make([]struct {
Type string
Port int32
}, 0)
for _, listener := range describeAllListenersResp.AllListenerList {
if listener.ListenerType == "HTTPS" || listener.ListenerType == "SSL" {
listeners = append(listeners, struct {
Type string
Port int32
}{
Type: listener.ListenerType,
Port: int32(listener.ListenerPort),
})
}
}
// 遍历更新监听证书
if len(listeners) == 0 {
d.logger.Info("no blb listeners to deploy")
} else {
d.logger.Info("found https/ssl listeners to deploy", slog.Any("listeners", listeners))
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)
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudListenerType string, cloudListenerPort int32, cloudCertId string) error {
switch strings.ToUpper(cloudListenerType) {
case "HTTPS":
return d.updateHttpsListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
case "SSL":
return d.updateSslListenerCertificate(ctx, cloudLoadbalancerId, cloudListenerPort, cloudCertId)
default:
return fmt.Errorf("unsupported listener type: %s", cloudListenerType)
}
}
func (d *DeployerProvider) updateHttpsListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudHttpsListenerPort int32, cloudCertId string) error {
// 查询 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#describehttpslisteners%E6%9F%A5%E8%AF%A2https%E7%9B%91%E5%90%AC%E5%99%A8
describeHTTPSListenersReq := &bceblb.DescribeListenerArgs{
ListenerPort: uint16(cloudHttpsListenerPort),
MaxKeys: 1,
}
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'")
} else if len(describeHTTPSListenersResp.ListenerList) == 0 {
return fmt.Errorf("listener %s:%d not found", cloudLoadbalancerId, cloudHttpsListenerPort)
}
if d.config.Domain == "" {
// 未指定 SNI只需部署到监听器
// 更新 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#updatehttpslistener%E6%9B%B4%E6%96%B0https%E7%9B%91%E5%90%AC%E5%99%A8
updateHTTPSListenerReq := &bceblb.UpdateHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
CertIds: []string{cloudCertId},
}
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'")
}
} else {
// 指定 SNI需部署到扩展域名
// 更新 HTTPS 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#updatehttpslistener%E6%9B%B4%E6%96%B0https%E7%9B%91%E5%90%AC%E5%99%A8
updateHTTPSListenerReq := &bceblb.UpdateHTTPSListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
AdditionalCertDomains: sliceutil.Map(describeHTTPSListenersResp.ListenerList[0].AdditionalCertDomains, func(domain bceblb.AdditionalCertDomainsModel) bceblb.AdditionalCertDomainsModel {
if domain.Host == d.config.Domain {
return bceblb.AdditionalCertDomainsModel{
Host: domain.Host,
CertId: cloudCertId,
}
}
return bceblb.AdditionalCertDomainsModel{
Host: domain.Host,
CertId: domain.CertId,
}
}),
}
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 nil
}
func (d *DeployerProvider) updateSslListenerCertificate(ctx context.Context, cloudLoadbalancerId string, cloudHttpsListenerPort int32, cloudCertId string) error {
// 更新 SSL 监听器
// REF: https://cloud.baidu.com/doc/BLB/s/yjwvxnvl6#updatessllistener%E6%9B%B4%E6%96%B0ssl%E7%9B%91%E5%90%AC%E5%99%A8
updateSSLListenerReq := &bceblb.UpdateSSLListenerArgs{
ClientToken: generateClientToken(),
ListenerPort: uint16(cloudHttpsListenerPort),
CertIds: []string{cloudCertId},
}
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 nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*bceblb.Client, error) {
endpoint := ""
if region != "" {
endpoint = fmt.Sprintf("blb.%s.baidubce.com", region)
}
client, err := bceblb.NewClient(accessKeyId, secretAccessKey, endpoint)
if err != nil {
return nil, err
}
return client, nil
}
func generateClientToken() string {
return strings.ReplaceAll(uuid.New().String(), "-", "")
}

View File

@@ -0,0 +1,86 @@
package baiducloudblb_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-blb"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fLoadbalancerId string
fDomain string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
}
/*
Shell command to run this test:
go test -v ./baiducloud_blb_test.go -args \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_SECRETACCESSKEY="your-secret-access-key" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_REGION="bj" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_LOADBALANCERID="your-blb-loadbalancer-id" \
--CERTIMATE_DEPLOYER_BAIDUCLOUDBLB_DOMAIN="your-blb-sni-domain"
*/
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("SECRETACCESSKEY: %v", fSecretAccessKey),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
fmt.Sprintf("DOMAIN: %v", fDomain),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
ResourceType: provider.RESOURCE_TYPE_LOADBALANCER,
Region: fRegion,
LoadbalancerId: fLoadbalancerId,
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 baiducloudblb
type ResourceType string
const (
// 资源类型:部署到指定负载均衡器。
RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
// 资源类型:部署到指定监听器。
RESOURCE_TYPE_LISTENER = ResourceType("listener")
)

View File

@@ -3,14 +3,14 @@
import (
"context"
"fmt"
"log/slog"
"time"
bceCdn "github.com/baidubce/bce-sdk-go/services/cdn"
bceCdnApi "github.com/baidubce/bce-sdk-go/services/cdn/api"
bcecdn "github.com/baidubce/bce-sdk-go/services/cdn"
bcecdnapi "github.com/baidubce/bce-sdk-go/services/cdn/api"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -24,8 +24,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *bceCdn.Client
logger *slog.Logger
sdkClient *bcecdn.Client
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -42,13 +42,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -57,24 +61,23 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8
putCertResp, err := d.sdkClient.PutCert(
d.config.Domain,
&bceCdnApi.UserCertificate{
&bcecdnapi.UserCertificate{
CertName: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
ServerData: certPem,
PrivateData: privkeyPem,
},
"ON",
)
d.logger.Debug("sdk request 'cdn.PutCert'", slog.String("request.domain", d.config.Domain), slog.Any("response", putCertResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.PutCert'")
}
d.logger.Logt("已修改域名证书", putCertResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, secretAccessKey string) (*bceCdn.Client, error) {
client, err := bceCdn.NewClient(accessKeyId, secretAccessKey, "")
func createSdkClient(accessKeyId, secretAccessKey string) (*bcecdn.Client, error) {
client, err := bcecdn.NewClient(accessKeyId, secretAccessKey, "")
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,68 @@
package baiducloudcert
import (
"context"
"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/baiducloud-cert"
)
type DeployerConfig struct {
// 百度智能云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 百度智能云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
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, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
return &deployer.DeployResult{}, nil
}

View File

@@ -2,14 +2,17 @@
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"regexp"
"strings"
"time"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
bssdk "github.com/usual2970/certimate/internal/pkg/vendors/baishan-sdk"
)
@@ -18,11 +21,14 @@ type DeployerConfig struct {
ApiToken string `json:"apiToken"`
// 加速域名(支持泛域名)。
Domain string `json:"domain"`
// 证书 ID。
// 选填。
CertificateId string `json:"certificateId,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *bssdk.Client
}
@@ -40,13 +46,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -55,53 +65,79 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return nil, errors.New("config `domain` is required")
}
// 查询域名配置
// REF: https://portal.baishancloud.com/track/document/api/1/1065
getDomainConfigReq := &bssdk.GetDomainConfigRequest{
Domains: d.config.Domain,
Config: "https",
}
getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.GetDomainConfig'")
} else if len(getDomainConfigResp.Data) == 0 {
return nil, errors.New("domain config not found")
} else {
d.logger.Logt("已查询到域名配置", getDomainConfigResp)
}
if d.config.CertificateId == "" {
// 新增证书
// REF: https://portal.baishancloud.com/track/document/downloadPdf/1441
certificateId := ""
createCertificateReq := &bssdk.CreateCertificateRequest{
Certificate: certPem,
Key: privkeyPem,
Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()),
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
d.logger.Debug("sdk request 'baishan.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
if createCertificateResp != nil {
if createCertificateResp.GetCode() == 400699 && strings.Contains(createCertificateResp.GetMessage(), "this certificate is exists") {
// 证书已存在,忽略新增证书接口错误
re := regexp.MustCompile(`\d+`)
certificateId = re.FindString(createCertificateResp.GetMessage())
}
}
// 新增证书
// REF: https://portal.baishancloud.com/track/document/downloadPdf/1441
createCertificateReq := &bssdk.CreateCertificateRequest{
Certificate: certPem,
Key: privkeyPem,
Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()),
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.CreateCertificate'")
} else {
d.logger.Logt("已新增证书", createCertificateResp)
}
if certificateId == "" {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.CreateCertificate'")
}
} else {
certificateId = createCertificateResp.Data.CertId.String()
}
// 设置域名配置
// REF: https://portal.baishancloud.com/track/document/api/1/1045
setDomainConfigReq := &bssdk.SetDomainConfigRequest{
Domains: d.config.Domain,
Config: &bssdk.DomainConfig{
Https: &bssdk.DomainConfigHttps{
CertId: createCertificateResp.Data.CertId,
ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps,
EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2,
EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp,
// 查询域名配置
// REF: https://portal.baishancloud.com/track/document/api/1/1065
getDomainConfigReq := &bssdk.GetDomainConfigRequest{
Domains: d.config.Domain,
Config: []string{"https"},
}
getDomainConfigResp, err := d.sdkClient.GetDomainConfig(getDomainConfigReq)
d.logger.Debug("sdk request 'baishan.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.GetDomainConfig'")
} else if len(getDomainConfigResp.Data) == 0 {
return nil, errors.New("domain config not found")
}
// 设置域名配置
// REF: https://portal.baishancloud.com/track/document/api/1/1045
setDomainConfigReq := &bssdk.SetDomainConfigRequest{
Domains: d.config.Domain,
Config: &bssdk.DomainConfig{
Https: &bssdk.DomainConfigHttps{
CertId: json.Number(certificateId),
ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps,
EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2,
EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp,
},
},
},
}
setDomainConfigResp, err := d.sdkClient.SetDomainConfig(setDomainConfigReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.SetDomainConfig'")
}
setDomainConfigResp, err := d.sdkClient.SetDomainConfig(setDomainConfigReq)
d.logger.Debug("sdk request 'baishan.SetDomainConfig'", slog.Any("request", setDomainConfigReq), slog.Any("response", setDomainConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.SetDomainConfig'")
}
} else {
d.logger.Logt("已设置域名配置", setDomainConfigResp)
// 替换证书
// REF: https://portal.baishancloud.com/track/document/downloadPdf/1441
createCertificateReq := &bssdk.CreateCertificateRequest{
CertificateId: &d.config.CertificateId,
Certificate: certPem,
Key: privkeyPem,
Name: fmt.Sprintf("certimate_%d", time.Now().UnixMilli()),
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
d.logger.Debug("sdk request 'baishan.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'baishan.CreateCertificate'")
}
}
return &deployer.DeployResult{}, nil

View File

@@ -2,13 +2,14 @@
import (
"context"
"crypto/tls"
"errors"
"log/slog"
"net/url"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk"
)
@@ -17,13 +18,15 @@ type DeployerConfig struct {
ApiUrl string `json:"apiUrl"`
// 宝塔面板接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
// 是否自动重启。
AutoRestart bool `json:"autoRestart"`
}
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *btsdk.Client
}
@@ -34,20 +37,24 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.ApiUrl, config.ApiKey)
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -58,28 +65,25 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
Certificate: certPem,
}
configSavePanelSSLResp, err := d.sdkClient.ConfigSavePanelSSL(configSavePanelSSLReq)
d.logger.Debug("sdk request 'bt.ConfigSavePanelSSL'", slog.Any("request", configSavePanelSSLReq), slog.Any("response", configSavePanelSSLResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.ConfigSavePanelSSL'")
} else {
d.logger.Logt("已设置面板 SSL 证书", configSavePanelSSLResp)
}
if d.config.AutoRestart {
// 重启面板
// 重启面板(无需关心响应,因为宝塔重启时会断开连接产生 error
systemServiceAdminReq := &btsdk.SystemServiceAdminRequest{
Name: "nginx",
Type: "restart",
}
_, err := d.sdkClient.SystemServiceAdmin(systemServiceAdminReq)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SystemServiceAdmin'")
}
systemServiceAdminResp, _ := d.sdkClient.SystemServiceAdmin(systemServiceAdminReq)
d.logger.Debug("sdk request 'bt.SystemServiceAdmin'", slog.Any("request", systemServiceAdminReq), slog.Any("response", systemServiceAdminResp))
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) {
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid baota api url")
}
@@ -89,5 +93,9 @@ func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) {
}
client := btsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}
return client, nil
}

View File

@@ -49,8 +49,10 @@ func TestDeploy(t *testing.T) {
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiKey: fApiKey,
ApiUrl: fApiUrl,
ApiKey: fApiKey,
AllowInsecureConnections: true,
AutoRestart: true,
})
if err != nil {
t.Errorf("err: %+v", err)

View File

@@ -2,15 +2,16 @@
import (
"context"
"crypto/tls"
"errors"
"fmt"
"log/slog"
"net/url"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/utils/slices"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk"
)
@@ -19,6 +20,8 @@ type DeployerConfig struct {
ApiUrl string `json:"apiUrl"`
// 宝塔面板接口密钥。
ApiKey string `json:"apiKey"`
// 是否允许不安全的连接。
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
// 站点类型。
SiteType string `json:"siteType"`
// 站点名称(单个)。
@@ -29,7 +32,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *btsdk.Client
}
@@ -40,20 +43,24 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client, err := createSdkClient(config.ApiUrl, config.ApiKey)
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -73,10 +80,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
PrivateKey: privkeyPem,
}
siteSetSSLResp, err := d.sdkClient.SiteSetSSL(siteSetSSLReq)
d.logger.Debug("sdk request 'bt.SiteSetSSL'", slog.Any("request", siteSetSSLReq), slog.Any("response", siteSetSSLResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SiteSetSSL'")
} else {
d.logger.Logt("已设置站点证书", siteSetSSLResp)
}
}
@@ -92,15 +98,14 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
PrivateKey: privkeyPem,
}
sslCertSaveCertResp, err := d.sdkClient.SSLCertSaveCert(sslCertSaveCertReq)
d.logger.Debug("sdk request 'bt.SSLCertSaveCert'", slog.Any("request", sslCertSaveCertReq), slog.Any("response", sslCertSaveCertResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLCertSaveCert'")
} else {
d.logger.Logt("已上传证书", sslCertSaveCertResp)
}
// 设置站点证书
sslSetBatchCertToSiteReq := &btsdk.SSLSetBatchCertToSiteRequest{
BatchInfo: slices.Map(d.config.SiteNames, func(siteName string) *btsdk.SSLSetBatchCertToSiteRequestBatchInfo {
BatchInfo: sliceutil.Map(d.config.SiteNames, func(siteName string) *btsdk.SSLSetBatchCertToSiteRequestBatchInfo {
return &btsdk.SSLSetBatchCertToSiteRequestBatchInfo{
SiteName: siteName,
SSLHash: sslCertSaveCertResp.SSLHash,
@@ -108,10 +113,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}),
}
sslSetBatchCertToSiteResp, err := d.sdkClient.SSLSetBatchCertToSite(sslSetBatchCertToSiteReq)
d.logger.Debug("sdk request 'bt.SSLSetBatchCertToSite'", slog.Any("request", sslSetBatchCertToSiteReq), slog.Any("response", sslSetBatchCertToSiteResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SSLSetBatchCertToSite'")
} else {
d.logger.Logt("已设置站点证书", sslSetBatchCertToSiteResp)
}
}
@@ -122,7 +126,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return &deployer.DeployResult{}, nil
}
func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) {
func createSdkClient(apiUrl, apiKey string, allowInsecure bool) (*btsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid baota api url")
}
@@ -132,5 +136,9 @@ func createSdkClient(apiUrl, apiKey string) (*btsdk.Client, error) {
}
client := btsdk.NewClient(apiUrl, apiKey)
if allowInsecure {
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
}
return client, nil
}

View File

@@ -57,11 +57,12 @@ func TestDeploy(t *testing.T) {
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
ApiUrl: fApiUrl,
ApiKey: fApiKey,
SiteType: fSiteType,
SiteName: fSiteName,
SiteNames: []string{fSiteName},
ApiUrl: fApiUrl,
ApiKey: fApiKey,
AllowInsecureConnections: true,
SiteType: fSiteType,
SiteName: fSiteName,
SiteNames: []string{fSiteName},
})
if err != nil {
t.Errorf("err: %+v", err)

View File

@@ -3,14 +3,13 @@
import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
bpCdn "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn"
bpcdn "github.com/byteplus-sdk/byteplus-sdk-golang/service/cdn"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/byteplus-cdn"
)
@@ -26,8 +25,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *bpCdn.CDN
logger *slog.Logger
sdkClient *bpcdn.CDN
sslUploader uploader.Uploader
}
@@ -38,7 +37,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
panic("config is nil")
}
client := bpCdn.NewInstance()
client := bpcdn.NewInstance()
client.Client.SetAccessKey(config.AccessKey)
client.Client.SetSecretKey(config.SecretKey)
@@ -52,14 +51,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -68,18 +72,19 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
domains := make([]string, 0)
if strings.HasPrefix(d.config.Domain, "*.") {
// 获取指定证书可关联的域名
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-describecertconfig-9ea17
describeCertConfigReq := &bpCdn.DescribeCertConfigRequest{
describeCertConfigReq := &bpcdn.DescribeCertConfigRequest{
CertId: upres.CertId,
}
describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.DescribeCertConfig'")
}
@@ -99,6 +104,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
if len(domains) == 0 {
if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 {
// 所有可关联的域名都配置了该证书,跳过部署
d.logger.Info("no domains to deploy")
} else {
return nil, errors.New("domain not found")
}
@@ -113,15 +119,14 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
for _, domain := range domains {
// 关联证书与加速域名
// REF: https://docs.byteplus.com/en/docs/byteplus-cdn/reference-batchdeploycert
batchDeployCertReq := &bpCdn.BatchDeployCertRequest{
batchDeployCertReq := &bpcdn.BatchDeployCertRequest{
CertId: upres.CertId,
Domain: domain,
}
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
if err != nil {
errs = append(errs, err)
} else {
d.logger.Logt(fmt.Sprintf("已关联证书到域名 %s", domain), batchDeployCertResp)
}
}

View File

@@ -3,11 +3,11 @@
import (
"context"
"errors"
"log/slog"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cachefly-sdk"
)
@@ -18,7 +18,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *cfsdk.Client
}
@@ -36,13 +36,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -53,10 +57,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
CertificateKey: privkeyPem,
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
d.logger.Debug("sdk request 'cachefly.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cachefly.CreateCertificate'")
} else {
d.logger.Logt("已上传证书", createCertificateResp)
}
return &deployer.DeployResult{}, nil

View File

@@ -5,13 +5,13 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/url"
"time"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
cfsdk "github.com/usual2970/certimate/internal/pkg/vendors/cdnfly-sdk"
)
@@ -34,7 +34,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *cfsdk.Client
}
@@ -52,13 +52,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -93,10 +97,9 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri
Id: d.config.SiteId,
}
getSiteResp, err := d.sdkClient.GetSite(getSiteReq)
d.logger.Debug("sdk request 'cdnfly.GetSite'", slog.Any("request", getSiteReq), slog.Any("response", getSiteResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.GetSite'")
} else {
d.logger.Logt("已获取网站详情", getSiteResp)
}
// 添加单个证书
@@ -108,10 +111,9 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri
Key: privkeyPem,
}
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
d.logger.Debug("sdk request 'cdnfly.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.CreateCertificate'")
} else {
d.logger.Logt("已添加证书", createCertificateResp)
}
// 修改单个网站
@@ -126,10 +128,9 @@ func (d *DeployerProvider) deployToSite(ctx context.Context, certPem string, pri
HttpsListen: &updateSiteHttpsListen,
}
updateSiteResp, err := d.sdkClient.UpdateSite(updateSiteReq)
d.logger.Debug("sdk request 'cdnfly.UpdateSite'", slog.Any("request", updateSiteReq), slog.Any("response", updateSiteResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateSite'")
} else {
d.logger.Logt("已修改网站", updateSiteResp)
}
return nil
@@ -150,10 +151,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri
Key: &privkeyPem,
}
updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq)
d.logger.Debug("sdk request 'cdnfly.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'cdnfly.UpdateCertificate'")
} else {
d.logger.Logt("已修改证书", updateCertificateResp)
}
return nil

View File

@@ -2,12 +2,12 @@
import (
"context"
"log/slog"
"strconv"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud"
dogesdk "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk"
@@ -24,7 +24,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *dogesdk.Client
sslUploader uploader.Uploader
}
@@ -48,14 +48,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -64,19 +69,18 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 绑定证书
// REF: https://docs.dogecloud.com/cdn/api-cert-bind
bindCdnCertId, _ := strconv.ParseInt(upres.CertId, 10, 64)
bindCdnCertResp, err := d.sdkClient.BindCdnCertWithDomain(bindCdnCertId, d.config.Domain)
d.logger.Debug("sdk request 'cdn.BindCdnCert'", slog.Int64("request.certId", bindCdnCertId), slog.String("request.domain", d.config.Domain), slog.Any("response", bindCdnCertResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.BindCdnCert'")
}
d.logger.Logt("已绑定证书", bindCdnCertResp)
return &deployer.DeployResult{}, nil
}

View File

@@ -2,14 +2,14 @@
import (
"context"
"log/slog"
edgio "github.com/Edgio/edgio-api/applications/v7"
edgiodtos "github.com/Edgio/edgio-api/applications/v7/dtos"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
edgsdk "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7"
edgsdkDtos "github.com/usual2970/certimate/internal/pkg/vendors/edgio-sdk/applications/v7/dtos"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
)
type DeployerConfig struct {
@@ -23,8 +23,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *edgsdk.EdgioClient
logger *slog.Logger
sdkClient *edgio.EdgioClient
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -41,42 +41,45 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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) {
// 提取 Edgio 所需的服务端证书和中间证书内容
privateCertPem, intermediateCertPem, err := certs.ExtractCertificatesFromPEM(certPem)
privateCertPem, intermediateCertPem, err := certutil.ExtractCertificatesFromPEM(certPem)
if err != nil {
return nil, err
}
// 上传 TLS 证书
// REF: https://docs.edg.io/rest_api/#tag/tls-certs/operation/postConfigV01TlsCerts
uploadTlsCertReq := edgsdkDtos.UploadTlsCertRequest{
uploadTlsCertReq := edgiodtos.UploadTlsCertRequest{
EnvironmentID: d.config.EnvironmentId,
PrimaryCert: privateCertPem,
IntermediateCert: intermediateCertPem,
PrivateKey: privkeyPem,
}
uploadTlsCertResp, err := d.sdkClient.UploadTlsCert(uploadTlsCertReq)
d.logger.Debug("sdk request 'edgio.UploadTlsCert'", slog.Any("request", uploadTlsCertReq), slog.Any("response", uploadTlsCertResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'edgio.UploadTlsCert'")
}
d.logger.Logt("已上传 TLS 证书", uploadTlsCertResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(clientId, clientSecret string) (*edgsdk.EdgioClient, error) {
client := edgsdk.NewEdgioClient(clientId, clientSecret, "", "")
func createSdkClient(clientId, clientSecret string) (*edgio.EdgioClient, error) {
client := edgio.NewEdgioClient(clientId, clientSecret, "", "")
return client, nil
}

View File

@@ -3,6 +3,7 @@
import (
"context"
"errors"
"log/slog"
"strconv"
gprovider "github.com/G-Core/gcorelabscdn-go/gcore/provider"
@@ -10,7 +11,6 @@ import (
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/gcore-cdn"
gcoresdk "github.com/usual2970/certimate/internal/pkg/vendors/gcore-sdk/common"
@@ -25,7 +25,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *gresources.Service
sslUploader uploader.Uploader
}
@@ -51,14 +51,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -72,16 +77,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 获取 CDN 资源详情
// REF: https://api.gcore.com/docs/cdn#tag/CDN-resources/paths/~1cdn~1resources~1%7Bresource_id%7D/get
getResourceResp, err := d.sdkClient.Get(context.TODO(), d.config.ResourceId)
d.logger.Debug("sdk request 'resources.Get'", slog.Any("resourceId", d.config.ResourceId), slog.Any("response", getResourceResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Get'")
} else {
d.logger.Logt("已获取 CDN 资源详情", getResourceResp)
}
// 更新 CDN 资源详情
@@ -96,15 +100,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
SSlEnabled: true,
SSLData: int(updateResourceCertId),
ProxySSLEnabled: getResourceResp.ProxySSLEnabled,
ProxySSLCA: &getResourceResp.ProxySSLCA,
ProxySSLData: &getResourceResp.ProxySSLData,
Options: getResourceResp.Options,
}
if getResourceResp.ProxySSLCA != 0 {
updateResourceReq.ProxySSLCA = &getResourceResp.ProxySSLCA
}
if getResourceResp.ProxySSLData != 0 {
updateResourceReq.ProxySSLData = &getResourceResp.ProxySSLData
}
if getResourceResp.Options != nil {
updateResourceReq.Options = getResourceResp.Options
}
updateResourceResp, err := d.sdkClient.Update(context.TODO(), d.config.ResourceId, updateResourceReq)
d.logger.Debug("sdk request 'resources.Update'", slog.Int64("resourceId", d.config.ResourceId), slog.Any("request", updateResourceReq), slog.Any("response", updateResourceResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'resources.Update'")
} else {
d.logger.Logt("已更新 CDN 资源详情", updateResourceResp)
}
return &deployer.DeployResult{}, nil

View File

@@ -2,15 +2,15 @@
import (
"context"
"log/slog"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
hcCdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
hcCdnRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region"
hccdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
hccdnmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
hccdnregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/region"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-scm"
hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk"
@@ -29,8 +29,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *hcCdn.CdnClient
logger *slog.Logger
sdkClient *hccdn.CdnClient
sslUploader uploader.Uploader
}
@@ -60,14 +60,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -76,48 +81,46 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 查询加速域名配置
// REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html
showDomainFullConfigReq := &hcCdnModel.ShowDomainFullConfigRequest{
showDomainFullConfigReq := &hccdnmodel.ShowDomainFullConfigRequest{
DomainName: d.config.Domain,
}
showDomainFullConfigResp, err := d.sdkClient.ShowDomainFullConfig(showDomainFullConfigReq)
d.logger.Debug("sdk request 'cdn.ShowDomainFullConfig'", slog.Any("request", showDomainFullConfigReq), slog.Any("response", showDomainFullConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ShowDomainFullConfig'")
}
d.logger.Logt("已查询到加速域名配置", showDomainFullConfigResp)
// 更新加速域名配置
// REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html
// REF: https://support.huaweicloud.com/usermanual-cdn/cdn_01_0306.html
updateDomainMultiCertificatesReqBodyContent := &hcCdnModel.UpdateDomainMultiCertificatesRequestBodyContent{}
updateDomainMultiCertificatesReqBodyContent := &hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent{}
updateDomainMultiCertificatesReqBodyContent.DomainName = d.config.Domain
updateDomainMultiCertificatesReqBodyContent.HttpsSwitch = 1
updateDomainMultiCertificatesReqBodyContent.CertificateType = hwsdk.Int32Ptr(2)
updateDomainMultiCertificatesReqBodyContent.ScmCertificateId = hwsdk.StringPtr(upres.CertId)
updateDomainMultiCertificatesReqBodyContent.CertName = hwsdk.StringPtr(upres.CertName)
updateDomainMultiCertificatesReqBodyContent = assign(updateDomainMultiCertificatesReqBodyContent, showDomainFullConfigResp.Configs)
updateDomainMultiCertificatesReq := &hcCdnModel.UpdateDomainMultiCertificatesRequest{
Body: &hcCdnModel.UpdateDomainMultiCertificatesRequestBody{
updateDomainMultiCertificatesReq := &hccdnmodel.UpdateDomainMultiCertificatesRequest{
Body: &hccdnmodel.UpdateDomainMultiCertificatesRequestBody{
Https: updateDomainMultiCertificatesReqBodyContent,
},
}
updateDomainMultiCertificatesResp, err := d.sdkClient.UpdateDomainMultiCertificates(updateDomainMultiCertificatesReq)
d.logger.Debug("sdk request 'cdn.UploadDomainMultiCertificates'", slog.Any("request", updateDomainMultiCertificatesReq), slog.Any("response", updateDomainMultiCertificatesResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadDomainMultiCertificates'")
}
d.logger.Logt("已更新加速域名配置", updateDomainMultiCertificatesResp)
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdn.CdnClient, error) {
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hccdn.CdnClient, error) {
if region == "" {
region = "cn-north-1" // CDN 服务默认区域:华北一北京
}
@@ -130,12 +133,12 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdn.CdnCli
return nil, err
}
hcRegion, err := hcCdnRegion.SafeValueOf(region)
hcRegion, err := hccdnregion.SafeValueOf(region)
if err != nil {
return nil, err
}
hcClient, err := hcCdn.CdnClientBuilder().
hcClient, err := hccdn.CdnClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
@@ -143,42 +146,44 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdn.CdnCli
return nil, err
}
client := hcCdn.NewCdnClient(hcClient)
client := hccdn.NewCdnClient(hcClient)
return client, nil
}
func assign(reqContent *hcCdnModel.UpdateDomainMultiCertificatesRequestBodyContent, target *hcCdnModel.ConfigsGetBody) *hcCdnModel.UpdateDomainMultiCertificatesRequestBodyContent {
func assign(source *hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent, target *hccdnmodel.ConfigsGetBody) *hccdnmodel.UpdateDomainMultiCertificatesRequestBodyContent {
// `UpdateDomainMultiCertificates` 中不传的字段表示使用默认值、而非保留原值,
// 因此这里需要把原配置中的参数重新赋值回去。
if target == nil {
return reqContent
return source
}
// 华为云 API 中不传的字段表示使用默认值、而非保留原值,因此这里需要把原配置中的参数重新赋值回去。
// 而且蛋疼的是查询接口返回的数据结构和更新接口传入的参数结构不一致,需要做很多转化。
if *target.OriginProtocol == "follow" {
reqContent.AccessOriginWay = hwsdk.Int32Ptr(1)
source.AccessOriginWay = hwsdk.Int32Ptr(1)
} else if *target.OriginProtocol == "http" {
reqContent.AccessOriginWay = hwsdk.Int32Ptr(2)
source.AccessOriginWay = hwsdk.Int32Ptr(2)
} else if *target.OriginProtocol == "https" {
reqContent.AccessOriginWay = hwsdk.Int32Ptr(3)
source.AccessOriginWay = hwsdk.Int32Ptr(3)
}
if target.ForceRedirect != nil {
reqContent.ForceRedirectConfig = &hcCdnModel.ForceRedirect{}
if source.ForceRedirectConfig == nil {
source.ForceRedirectConfig = &hccdnmodel.ForceRedirect{}
}
if target.ForceRedirect.Status == "on" {
reqContent.ForceRedirectConfig.Switch = 1
reqContent.ForceRedirectConfig.RedirectType = target.ForceRedirect.Type
source.ForceRedirectConfig.Switch = 1
source.ForceRedirectConfig.RedirectType = target.ForceRedirect.Type
} else {
reqContent.ForceRedirectConfig.Switch = 0
source.ForceRedirectConfig.Switch = 0
}
}
if target.Https != nil {
if *target.Https.Http2Status == "on" {
reqContent.Http2 = hwsdk.Int32Ptr(1)
source.Http2 = hwsdk.Int32Ptr(1)
}
}
return reqContent
return source
}

View File

@@ -4,20 +4,20 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hcElb "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3"
hcElbModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/model"
hcElbRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/region"
hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hcelb "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3"
hcelbmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/model"
hcelbregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/elb/v3/region"
hciam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hciammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hciamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
xerrors "github.com/pkg/errors"
"golang.org/x/exp/slices"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-elb"
hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk"
@@ -45,8 +45,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *hcElb.ElbClient
logger *slog.Logger
sdkClient *hcelb.ElbClient
sslUploader uploader.Uploader
}
@@ -73,26 +73,23 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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) {
// 上传证书到 SCM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
}
d.logger.Logt("certificate file uploaded", upres)
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_CERTIFICATE:
@@ -124,22 +121,21 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri
// 更新证书
// REF: https://support.huaweicloud.com/api-elb/UpdateCertificate.html
updateCertificateReq := &hcElbModel.UpdateCertificateRequest{
updateCertificateReq := &hcelbmodel.UpdateCertificateRequest{
CertificateId: d.config.CertificateId,
Body: &hcElbModel.UpdateCertificateRequestBody{
Certificate: &hcElbModel.UpdateCertificateOption{
Body: &hcelbmodel.UpdateCertificateRequestBody{
Certificate: &hcelbmodel.UpdateCertificateOption{
Certificate: hwsdk.StringPtr(certPem),
PrivateKey: hwsdk.StringPtr(privkeyPem),
},
},
}
updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq)
d.logger.Debug("sdk request 'elb.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateCertificate'")
}
d.logger.Logt("已更新 ELB 证书", updateCertificateResp)
return nil
}
@@ -150,29 +146,29 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str
// 查询负载均衡器详情
// REF: https://support.huaweicloud.com/api-elb/ShowLoadBalancer.html
showLoadBalancerReq := &hcElbModel.ShowLoadBalancerRequest{
showLoadBalancerReq := &hcelbmodel.ShowLoadBalancerRequest{
LoadbalancerId: d.config.LoadbalancerId,
}
showLoadBalancerResp, err := d.sdkClient.ShowLoadBalancer(showLoadBalancerReq)
d.logger.Debug("sdk request 'elb.ShowLoadBalancer'", slog.Any("request", showLoadBalancerReq), slog.Any("response", showLoadBalancerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowLoadBalancer'")
}
d.logger.Logt("已查询到 ELB 负载均衡器", showLoadBalancerResp)
// 查询监听器列表
// REF: https://support.huaweicloud.com/api-elb/ListListeners.html
listenerIds := make([]string, 0)
listListenersLimit := int32(2000)
var listListenersMarker *string = nil
for {
listListenersReq := &hcElbModel.ListListenersRequest{
listListenersReq := &hcelbmodel.ListListenersRequest{
Limit: hwsdk.Int32Ptr(listListenersLimit),
Marker: listListenersMarker,
Protocol: &[]string{"HTTPS", "TERMINATED_HTTPS"},
LoadbalancerId: &[]string{showLoadBalancerResp.Loadbalancer.Id},
}
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'elb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListListeners'")
}
@@ -190,20 +186,19 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPem str
}
}
d.logger.Logt("已查询到 ELB 负载均衡器下的监听器", listenerIds)
// 上传证书到 SCM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 遍历更新监听器证书
if len(listenerIds) == 0 {
return errors.New("listener not found")
d.logger.Info("no listeners to deploy")
} else {
d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
var errs []error
for _, listenerId := range listenerIds {
@@ -229,10 +224,10 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, certPem string,
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// 更新监听器证书
if err := d.modifyListenerCertificate(ctx, d.config.ListenerId, upres.CertId); err != nil {
return err
@@ -244,22 +239,21 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, certPem string,
func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
// 查询监听器详情
// REF: https://support.huaweicloud.com/api-elb/ShowListener.html
showListenerReq := &hcElbModel.ShowListenerRequest{
showListenerReq := &hcelbmodel.ShowListenerRequest{
ListenerId: cloudListenerId,
}
showListenerResp, err := d.sdkClient.ShowListener(showListenerReq)
d.logger.Debug("sdk request 'elb.ShowListener'", slog.Any("request", showListenerReq), slog.Any("response", showListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowListener'")
}
d.logger.Logt("已查询到 ELB 监听器", showListenerResp)
// 更新监听器
// REF: https://support.huaweicloud.com/api-elb/UpdateListener.html
updateListenerReq := &hcElbModel.UpdateListenerRequest{
updateListenerReq := &hcelbmodel.UpdateListenerRequest{
ListenerId: cloudListenerId,
Body: &hcElbModel.UpdateListenerRequestBody{
Listener: &hcElbModel.UpdateListenerOption{
Body: &hcelbmodel.UpdateListenerRequestBody{
Listener: &hcelbmodel.UpdateListenerOption{
DefaultTlsContainerRef: hwsdk.StringPtr(cloudCertId),
},
},
@@ -270,18 +264,20 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL
sniCertIds := make([]string, 0)
sniCertIds = append(sniCertIds, cloudCertId)
listOldCertificateReq := &hcElbModel.ListCertificatesRequest{
listOldCertificateReq := &hcelbmodel.ListCertificatesRequest{
Id: &showListenerResp.Listener.SniContainerRefs,
}
listOldCertificateResp, err := d.sdkClient.ListCertificates(listOldCertificateReq)
d.logger.Debug("sdk request 'elb.ListCertificates'", slog.Any("request", listOldCertificateReq), slog.Any("response", listOldCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.ListCertificates'")
}
showNewCertificateReq := &hcElbModel.ShowCertificateRequest{
showNewCertificateReq := &hcelbmodel.ShowCertificateRequest{
CertificateId: cloudCertId,
}
showNewCertificateResp, err := d.sdkClient.ShowCertificate(showNewCertificateReq)
d.logger.Debug("sdk request 'elb.ShowCertificate'", slog.Any("request", showNewCertificateReq), slog.Any("response", showNewCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.ShowCertificate'")
}
@@ -311,16 +307,15 @@ func (d *DeployerProvider) modifyListenerCertificate(ctx context.Context, cloudL
}
}
updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'elb.UpdateListener'")
}
d.logger.Logt("已更新 ELB 监听器", updateListenerResp)
return nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcElb.ElbClient, error) {
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcelb.ElbClient, error) {
projectId, err := getSdkProjectId(accessKeyId, secretAccessKey, region)
if err != nil {
return nil, err
@@ -335,12 +330,12 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcElb.ElbCli
return nil, err
}
hcRegion, err := hcElbRegion.SafeValueOf(region)
hcRegion, err := hcelbregion.SafeValueOf(region)
if err != nil {
return nil, err
}
hcClient, err := hcElb.ElbClientBuilder().
hcClient, err := hcelb.ElbClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
@@ -348,7 +343,7 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcElb.ElbCli
return nil, err
}
client := hcElb.NewElbClient(hcClient)
client := hcelb.NewElbClient(hcClient)
return client, nil
}
@@ -365,12 +360,12 @@ func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error
return "", err
}
hcRegion, err := hcIamRegion.SafeValueOf(region)
hcRegion, err := hciamregion.SafeValueOf(region)
if err != nil {
return "", err
}
hcClient, err := hcIam.IamClientBuilder().
hcClient, err := hciam.IamClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
@@ -378,9 +373,9 @@ func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error
return "", err
}
client := hcIam.NewIamClient(hcClient)
client := hciam.NewIamClient(hcClient)
request := &hcIamModel.KeystoneListProjectsRequest{
request := &hciammodel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)

View File

@@ -0,0 +1,69 @@
package huaweicloudscm
import (
"context"
"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/huaweicloud-scm"
)
type DeployerConfig struct {
// 华为云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
})
if err != nil {
return nil, xerrors.Wrap(err, "failed to create ssl uploader")
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
d.sslUploader.WithLogger(logger)
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
// 上传证书到 SCM
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
return &deployer.DeployResult{}, nil
}

View File

@@ -4,20 +4,20 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hcIam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hcIamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hcIamRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hcWaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1"
hcWafModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model"
hcWafRegion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region"
hciam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hciamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hciamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hcwaf "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1"
hcwafmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model"
hcwafregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/huaweicloud-waf"
hwsdk "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-sdk"
@@ -42,8 +42,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *hcWaf.WafClient
logger *slog.Logger
sdkClient *hcwaf.WafClient
sslUploader uploader.Uploader
}
@@ -70,14 +70,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -87,7 +92,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 根据部署资源类型决定部署方式
@@ -121,31 +126,29 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPem stri
// 查询证书
// REF: https://support.huaweicloud.com/api-waf/ShowCertificate.html
showCertificateReq := &hcWafModel.ShowCertificateRequest{
showCertificateReq := &hcwafmodel.ShowCertificateRequest{
CertificateId: d.config.CertificateId,
}
showCertificateResp, err := d.sdkClient.ShowCertificate(showCertificateReq)
d.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", showCertificateReq), slog.Any("response", showCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.ShowCertificate'")
} else {
d.logger.Logt("已获取 WAF 证书", showCertificateResp)
}
// 更新证书
// REF: https://support.huaweicloud.com/api-waf/UpdateCertificate.html
updateCertificateReq := &hcWafModel.UpdateCertificateRequest{
updateCertificateReq := &hcwafmodel.UpdateCertificateRequest{
CertificateId: d.config.CertificateId,
Body: &hcWafModel.UpdateCertificateRequestBody{
Body: &hcwafmodel.UpdateCertificateRequestBody{
Name: *showCertificateResp.Name,
Content: hwsdk.StringPtr(certPem),
Key: hwsdk.StringPtr(privkeyPem),
},
}
updateCertificateResp, err := d.sdkClient.UpdateCertificate(updateCertificateReq)
d.logger.Debug("sdk request 'waf.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateCertificate'")
} else {
d.logger.Logt("已更新 WAF 证书", updateCertificateResp)
}
return nil
@@ -161,7 +164,7 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem stri
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 遍历查询云模式防护域名列表,获取防护域名 ID
@@ -170,12 +173,13 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem stri
listHostPage := int32(1)
listHostPageSize := int32(100)
for {
listHostReq := &hcWafModel.ListHostRequest{
listHostReq := &hcwafmodel.ListHostRequest{
Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: hwsdk.Int32Ptr(listHostPage),
Pagesize: hwsdk.Int32Ptr(listHostPageSize),
}
listHostResp, err := d.sdkClient.ListHost(listHostReq)
d.logger.Debug("sdk request 'waf.ListHost'", slog.Any("request", listHostReq), slog.Any("response", listHostResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListHost'")
}
@@ -201,18 +205,17 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPem stri
// 更新云模式防护域名的配置
// REF: https://support.huaweicloud.com/api-waf/UpdateHost.html
updateHostReq := &hcWafModel.UpdateHostRequest{
updateHostReq := &hcwafmodel.UpdateHostRequest{
InstanceId: hostId,
Body: &hcWafModel.UpdateHostRequestBody{
Body: &hcwafmodel.UpdateHostRequestBody{
Certificateid: hwsdk.StringPtr(upres.CertId),
Certificatename: hwsdk.StringPtr(upres.CertName),
},
}
updateHostResp, err := d.sdkClient.UpdateHost(updateHostReq)
d.logger.Debug("sdk request 'waf.UpdateHost'", slog.Any("request", updateHostReq), slog.Any("response", updateHostResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdateHost'")
} else {
d.logger.Logt("已更新云模式防护域名的配置", updateHostResp)
}
return nil
@@ -228,7 +231,7 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem stri
if err != nil {
return xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 遍历查询独享模式域名列表,获取防护域名 ID
@@ -237,12 +240,13 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem stri
listPremiumHostPage := int32(1)
listPremiumHostPageSize := int32(100)
for {
listPremiumHostReq := &hcWafModel.ListPremiumHostRequest{
listPremiumHostReq := &hcwafmodel.ListPremiumHostRequest{
Hostname: hwsdk.StringPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPage)),
Pagesize: hwsdk.StringPtr(fmt.Sprintf("%d", listPremiumHostPageSize)),
}
listPremiumHostResp, err := d.sdkClient.ListPremiumHost(listPremiumHostReq)
d.logger.Debug("sdk request 'waf.ListPremiumHost'", slog.Any("request", listPremiumHostReq), slog.Any("response", listPremiumHostResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.ListPremiumHost'")
}
@@ -268,24 +272,23 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPem stri
// 修改独享模式域名配置
// REF: https://support.huaweicloud.com/api-waf/UpdatePremiumHost.html
updatePremiumHostReq := &hcWafModel.UpdatePremiumHostRequest{
updatePremiumHostReq := &hcwafmodel.UpdatePremiumHostRequest{
HostId: hostId,
Body: &hcWafModel.UpdatePremiumHostRequestBody{
Body: &hcwafmodel.UpdatePremiumHostRequestBody{
Certificateid: hwsdk.StringPtr(upres.CertId),
Certificatename: hwsdk.StringPtr(upres.CertName),
},
}
updatePremiumHostResp, err := d.sdkClient.UpdatePremiumHost(updatePremiumHostReq)
d.logger.Debug("sdk request 'waf.UpdatePremiumHost'", slog.Any("request", updatePremiumHostReq), slog.Any("response", updatePremiumHostResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'waf.UpdatePremiumHost'")
} else {
d.logger.Logt("已修改独享模式域名配置", updatePremiumHostResp)
}
return nil
}
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcWaf.WafClient, error) {
func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcwaf.WafClient, error) {
projectId, err := getSdkProjectId(accessKeyId, secretAccessKey, region)
if err != nil {
return nil, err
@@ -300,12 +303,12 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcWaf.WafCli
return nil, err
}
hcRegion, err := hcWafRegion.SafeValueOf(region)
hcRegion, err := hcwafregion.SafeValueOf(region)
if err != nil {
return nil, err
}
hcClient, err := hcWaf.WafClientBuilder().
hcClient, err := hcwaf.WafClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
@@ -313,7 +316,7 @@ func createSdkClient(accessKeyId, secretAccessKey, region string) (*hcWaf.WafCli
return nil, err
}
client := hcWaf.NewWafClient(hcClient)
client := hcwaf.NewWafClient(hcClient)
return client, nil
}
@@ -326,12 +329,12 @@ func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error
return "", err
}
hcRegion, err := hcIamRegion.SafeValueOf(region)
hcRegion, err := hciamregion.SafeValueOf(region)
if err != nil {
return "", err
}
hcClient, err := hcIam.IamClientBuilder().
hcClient, err := hciam.IamClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
@@ -339,9 +342,9 @@ func getSdkProjectId(accessKeyId, secretAccessKey, region string) (string, error
return "", err
}
client := hcIam.NewIamClient(hcClient)
client := hciam.NewIamClient(hcClient)
request := &hcIamModel.KeystoneListProjectsRequest{
request := &hciamModel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)

View File

@@ -4,20 +4,20 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdCommon "github.com/jdcloud-api/jdcloud-sdk-go/services/common/models"
jdLbApi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis"
jdLbClient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client"
jdLbModel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdcommon "github.com/jdcloud-api/jdcloud-sdk-go/services/common/models"
jdlbapi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis"
jdlbclient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client"
jdlbmodel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl"
"github.com/usual2970/certimate/internal/pkg/utils/slices"
"github.com/usual2970/certimate/internal/pkg/utils/sliceutil"
)
type DeployerConfig struct {
@@ -42,8 +42,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *jdLbClient.LbClient
logger *slog.Logger
sdkClient *jdlbclient.LbClient
sslUploader uploader.Uploader
}
@@ -69,14 +69,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -86,7 +91,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 根据部署资源类型决定部署方式
@@ -115,12 +120,11 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
// 查询负载均衡器详情
// REF: https://docs.jdcloud.com/cn/load-balancer/api/describeloadbalancer
describeLoadBalancerReq := jdLbApi.NewDescribeLoadBalancerRequest(d.config.RegionId, d.config.LoadbalancerId)
describeLoadBalancerReq := jdlbapi.NewDescribeLoadBalancerRequest(d.config.RegionId, d.config.LoadbalancerId)
describeLoadBalancerResp, err := d.sdkClient.DescribeLoadBalancer(describeLoadBalancerReq)
d.logger.Debug("sdk request 'lb.DescribeLoadBalancer'", slog.Any("request", describeLoadBalancerReq), slog.Any("response", describeLoadBalancerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeLoadBalancer'")
} else {
d.logger.Logt("已查询到负载均衡器详情", describeLoadBalancerResp)
}
// 查询监听器列表
@@ -129,11 +133,12 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
describeListenersPageNumber := 1
describeListenersPageSize := 100
for {
describeListenersReq := jdLbApi.NewDescribeListenersRequest(d.config.RegionId)
describeListenersReq.SetFilters([]jdCommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}})
describeListenersReq := jdlbapi.NewDescribeListenersRequest(d.config.RegionId)
describeListenersReq.SetFilters([]jdcommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}})
describeListenersReq.SetPageSize(describeListenersPageNumber)
describeListenersReq.SetPageSize(describeListenersPageSize)
describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq)
d.logger.Debug("sdk request 'lb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenersResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListeners'")
}
@@ -153,9 +158,9 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
// 遍历更新监听器证书
if len(listenerIds) == 0 {
return errors.New("listener not found")
d.logger.Info("no listeners to deploy")
} else {
d.logger.Logt("已查询到负载均衡器下的全部 HTTPS/TLS 监听器", listenerIds)
d.logger.Info("found https/tls listeners to deploy", slog.Any("listenerIds", listenerIds))
var errs []error
@@ -189,12 +194,11 @@ func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId str
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
// 查询监听器详情
// REF: https://docs.jdcloud.com/cn/load-balancer/api/describelistener
describeListenerReq := jdLbApi.NewDescribeListenerRequest(d.config.RegionId, cloudListenerId)
describeListenerReq := jdlbapi.NewDescribeListenerRequest(d.config.RegionId, cloudListenerId)
describeListenerResp, err := d.sdkClient.DescribeListener(describeListenerReq)
d.logger.Debug("sdk request 'lb.DescribeListener'", slog.Any("request", describeListenerReq), slog.Any("response", describeListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListener'")
} else {
d.logger.Logt("已查询到监听器详情", describeListenerResp)
}
if d.config.Domain == "" {
@@ -202,18 +206,17 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
// 修改监听器信息
// REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistener
updateListenerReq := jdLbApi.NewUpdateListenerRequest(d.config.RegionId, cloudListenerId)
updateListenerReq.SetCertificateSpecs([]jdLbModel.CertificateSpec{{CertificateId: cloudCertId}})
updateListenerReq := jdlbapi.NewUpdateListenerRequest(d.config.RegionId, cloudListenerId)
updateListenerReq.SetCertificateSpecs([]jdlbmodel.CertificateSpec{{CertificateId: cloudCertId}})
updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
d.logger.Debug("sdk request 'lb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListener'")
} else {
d.logger.Logt("已修改监听器信息", updateListenerResp)
}
} else {
// 指定 SNI需部署到扩展证书
extCertSpecs := slices.Filter(describeListenerResp.Result.Listener.ExtensionCertificateSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) bool {
extCertSpecs := sliceutil.Filter(describeListenerResp.Result.Listener.ExtensionCertificateSpecs, func(extCertSpec jdlbmodel.ExtensionCertificateSpec) bool {
return extCertSpec.Domain == d.config.Domain
})
if len(extCertSpecs) == 0 {
@@ -222,11 +225,11 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
// 批量修改扩展证书
// REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistenercertificates
updateListenerCertificatesReq := jdLbApi.NewUpdateListenerCertificatesRequest(
updateListenerCertificatesReq := jdlbapi.NewUpdateListenerCertificatesRequest(
d.config.RegionId,
cloudListenerId,
slices.Map(extCertSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) jdLbModel.ExtCertificateUpdateSpec {
return jdLbModel.ExtCertificateUpdateSpec{
sliceutil.Map(extCertSpecs, func(extCertSpec jdlbmodel.ExtensionCertificateSpec) jdlbmodel.ExtCertificateUpdateSpec {
return jdlbmodel.ExtCertificateUpdateSpec{
CertificateBindId: extCertSpec.CertificateBindId,
CertificateId: &cloudCertId,
Domain: &extCertSpec.Domain,
@@ -234,19 +237,18 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL
}),
)
updateListenerCertificatesResp, err := d.sdkClient.UpdateListenerCertificates(updateListenerCertificatesReq)
d.logger.Debug("sdk request 'lb.UpdateListenerCertificates'", slog.Any("request", updateListenerCertificatesReq), slog.Any("response", updateListenerCertificatesResp))
if err != nil {
return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListenerCertificates'")
} else {
d.logger.Logt("已批量修改扩展证书", updateListenerCertificatesResp)
}
}
return nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*jdLbClient.LbClient, error) {
clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret)
client := jdLbClient.NewLbClient(clientCredentials)
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
func createSdkClient(accessKeyId, accessKeySecret string) (*jdlbclient.LbClient, error) {
clientCredentials := jdcore.NewCredentials(accessKeyId, accessKeySecret)
client := jdlbclient.NewLbClient(clientCredentials)
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return client, nil
}

View File

@@ -2,14 +2,14 @@
import (
"context"
"log/slog"
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdCdnApi "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/apis"
jdCdnClient "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/client"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdcdnapi "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/apis"
jdcdnclient "github.com/jdcloud-api/jdcloud-sdk-go/services/cdn/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl"
)
@@ -25,8 +25,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *jdCdnClient.CdnClient
logger *slog.Logger
sdkClient *jdcdnclient.CdnClient
sslUploader uploader.Uploader
}
@@ -52,26 +52,30 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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) {
// 查询域名配置信息
// REF: https://docs.jdcloud.com/cn/cdn/api/querydomainconfig
queryDomainConfigReq := jdCdnApi.NewQueryDomainConfigRequest(d.config.Domain)
queryDomainConfigReq := jdcdnapi.NewQueryDomainConfigRequest(d.config.Domain)
queryDomainConfigResp, err := d.sdkClient.QueryDomainConfig(queryDomainConfigReq)
d.logger.Debug("sdk request 'cdn.QueryDomainConfig'", slog.Any("request", queryDomainConfigReq), slog.Any("response", queryDomainConfigResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.QueryDomainConfig'")
} else {
d.logger.Logt("已查询到域名配置信息", queryDomainConfigResp)
}
// 上传证书到 SSL
@@ -79,12 +83,12 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Logt("certificate file uploaded", upres)
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 设置通讯协议
// REF: https://docs.jdcloud.com/cn/cdn/api/sethttptype
setHttpTypeReq := jdCdnApi.NewSetHttpTypeRequest(d.config.Domain)
setHttpTypeReq := jdcdnapi.NewSetHttpTypeRequest(d.config.Domain)
setHttpTypeReq.SetHttpType("https")
setHttpTypeReq.SetCertificate(certPem)
setHttpTypeReq.SetRsaKey(privkeyPem)
@@ -92,18 +96,17 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
setHttpTypeReq.SetSslCertId(upres.CertId)
setHttpTypeReq.SetJumpType(queryDomainConfigResp.Result.HttpsJumpType)
setHttpTypeResp, err := d.sdkClient.SetHttpType(setHttpTypeReq)
d.logger.Debug("sdk request 'cdn.QueryDomainConfig'", slog.Any("request", setHttpTypeReq), slog.Any("response", setHttpTypeResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.SetHttpType'")
} else {
d.logger.Logt("已设置通讯协议", setHttpTypeResp)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*jdCdnClient.CdnClient, error) {
clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret)
client := jdCdnClient.NewCdnClient(clientCredentials)
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
func createSdkClient(accessKeyId, accessKeySecret string) (*jdcdnclient.CdnClient, error) {
clientCredentials := jdcore.NewCredentials(accessKeyId, accessKeySecret)
client := jdcdnclient.NewCdnClient(clientCredentials)
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return client, nil
}

View File

@@ -2,14 +2,14 @@
import (
"context"
"log/slog"
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdLiveApi "github.com/jdcloud-api/jdcloud-sdk-go/services/live/apis"
jdLiveClient "github.com/jdcloud-api/jdcloud-sdk-go/services/live/client"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdliveapi "github.com/jdcloud-api/jdcloud-sdk-go/services/live/apis"
jdliveclient "github.com/jdcloud-api/jdcloud-sdk-go/services/live/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -23,8 +23,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *jdLiveClient.LiveClient
logger *slog.Logger
sdkClient *jdliveclient.LiveClient
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -41,35 +41,38 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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) {
// 设置直播证书
// REF: https://docs.jdcloud.com/cn/live-video/api/setlivedomaincertificate
setLiveDomainCertificateReq := jdLiveApi.NewSetLiveDomainCertificateRequest(d.config.Domain, "on")
setLiveDomainCertificateReq := jdliveapi.NewSetLiveDomainCertificateRequest(d.config.Domain, "on")
setLiveDomainCertificateReq.SetCert(certPem)
setLiveDomainCertificateReq.SetKey(privkeyPem)
setLiveDomainCertificateResp, err := d.sdkClient.SetLiveDomainCertificate(setLiveDomainCertificateReq)
d.logger.Debug("sdk request 'live.SetLiveDomainCertificate'", slog.Any("request", setLiveDomainCertificateReq), slog.Any("response", setLiveDomainCertificateResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'live.SetLiveDomainCertificate'")
} else {
d.logger.Logt("已设置直播证书", setLiveDomainCertificateResp)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*jdLiveClient.LiveClient, error) {
clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret)
client := jdLiveClient.NewLiveClient(clientCredentials)
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
func createSdkClient(accessKeyId, accessKeySecret string) (*jdliveclient.LiveClient, error) {
clientCredentials := jdcore.NewCredentials(accessKeyId, accessKeySecret)
client := jdliveclient.NewLiveClient(clientCredentials)
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return client, nil
}

View File

@@ -3,16 +3,16 @@
import (
"context"
"fmt"
"log/slog"
"strconv"
"time"
jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdVodApi "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/apis"
jdVodClient "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/client"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdvodapi "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/apis"
jdvodclient "github.com/jdcloud-api/jdcloud-sdk-go/services/vod/client"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
)
type DeployerConfig struct {
@@ -26,8 +26,8 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
sdkClient *jdVodClient.VodClient
logger *slog.Logger
sdkClient *jdvodclient.VodClient
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -44,13 +44,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -61,10 +65,11 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
listDomainsPageNumber := 1
listDomainsPageSize := 100
for {
listDomainsReq := jdVodApi.NewListDomainsRequest()
listDomainsReq := jdvodapi.NewListDomainsRequest()
listDomainsReq.SetPageNumber(1)
listDomainsReq.SetPageSize(100)
listDomainsResp, err := d.sdkClient.ListDomains(listDomainsReq)
d.logger.Debug("sdk request 'vod.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.ListDomains'")
}
@@ -88,17 +93,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 查询域名 SSL 配置
// REF: https://docs.jdcloud.com/cn/video-on-demand/api/gethttpssl
getHttpSslReq := jdVodApi.NewGetHttpSslRequest(domainId)
getHttpSslReq := jdvodapi.NewGetHttpSslRequest(domainId)
getHttpSslResp, err := d.sdkClient.GetHttpSsl(getHttpSslReq)
d.logger.Debug("sdk request 'vod.GetHttpSsl'", slog.Any("request", getHttpSslReq), slog.Any("response", getHttpSslResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.GetHttpSsl'")
} else {
d.logger.Logt("已查询到域名 SSL 配置", getHttpSslResp)
}
// 设置域名 SSL 配置
// REF: https://docs.jdcloud.com/cn/video-on-demand/api/sethttpssl
setHttpSslReq := jdVodApi.NewSetHttpSslRequest(domainId)
setHttpSslReq := jdvodapi.NewSetHttpSslRequest(domainId)
setHttpSslReq.SetTitle(fmt.Sprintf("certimate-%d", time.Now().UnixMilli()))
setHttpSslReq.SetSslCert(certPem)
setHttpSslReq.SetSslKey(privkeyPem)
@@ -106,18 +110,17 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
setHttpSslReq.SetJumpType(getHttpSslResp.Result.JumpType)
setHttpSslReq.SetEnabled(true)
setHttpSslResp, err := d.sdkClient.SetHttpSsl(setHttpSslReq)
d.logger.Debug("sdk request 'vod.SetHttpSsl'", slog.Any("request", setHttpSslReq), slog.Any("response", setHttpSslResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'vod.SetHttpSsl'")
} else {
d.logger.Logt("已设置域名 SSL 配置", setHttpSslResp)
}
return &deployer.DeployResult{}, nil
}
func createSdkClient(accessKeyId, accessKeySecret string) (*jdVodClient.VodClient, error) {
clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret)
client := jdVodClient.NewVodClient(clientCredentials)
client.SetLogger(jdCore.NewDefaultLogger(jdCore.LogWarn))
func createSdkClient(accessKeyId, accessKeySecret string) (*jdvodclient.VodClient, error) {
clientCredentials := jdcore.NewCredentials(accessKeyId, accessKeySecret)
client := jdvodclient.NewVodClient(clientCredentials)
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
return client, nil
}

View File

@@ -1,20 +1,20 @@
package k8ssecret
package k8ssecret
import (
"context"
"errors"
"log/slog"
"strings"
xerrors "github.com/pkg/errors"
k8sCore "k8s.io/api/core/v1"
k8sMeta "k8s.io/apimachinery/pkg/apis/meta/v1"
k8score "k8s.io/api/core/v1"
k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
)
type DeployerConfig struct {
@@ -34,7 +34,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -45,13 +45,17 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
return &DeployerProvider{
logger: logger.NewNilLogger(),
logger: slog.Default(),
config: config,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -72,7 +76,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return nil, errors.New("config `secretDataKeyForKey` is required")
}
certX509, err := certs.ParseCertificateFromPEM(certPem)
certX509, err := certutil.ParseCertificateFromPEM(certPem)
if err != nil {
return nil, err
}
@@ -83,7 +87,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
return nil, xerrors.Wrap(err, "failed to create k8s client")
}
var secretPayload *k8sCore.Secret
var secretPayload *k8score.Secret
secretAnnotations := map[string]string{
"certimate/common-name": certX509.Subject.CommonName,
"certimate/subject-sn": certX509.Subject.SerialNumber,
@@ -93,34 +97,34 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
// 获取 Secret 实例,如果不存在则创建
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Get(context.TODO(), d.config.SecretName, k8sMeta.GetOptions{})
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Get(context.TODO(), d.config.SecretName, k8smeta.GetOptions{})
if err != nil {
secretPayload = &k8sCore.Secret{
TypeMeta: k8sMeta.TypeMeta{
secretPayload = &k8score.Secret{
TypeMeta: k8smeta.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: k8sMeta.ObjectMeta{
ObjectMeta: k8smeta.ObjectMeta{
Name: d.config.SecretName,
Annotations: secretAnnotations,
},
Type: k8sCore.SecretType(d.config.SecretType),
Type: k8score.SecretType(d.config.SecretType),
}
secretPayload.Data = make(map[string][]byte)
secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPem)
secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPem)
_, err = client.CoreV1().Secrets(d.config.Namespace).Create(context.TODO(), secretPayload, k8sMeta.CreateOptions{})
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Create(context.TODO(), secretPayload, k8smeta.CreateOptions{})
d.logger.Debug("k8s operate 'Secrets.Create'", slog.String("namespace", d.config.Namespace), slog.Any("secret", secretPayload))
if err != nil {
return nil, xerrors.Wrap(err, "failed to create k8s secret")
} else {
d.logger.Logf("k8s secret created", secretPayload)
return &deployer.DeployResult{}, nil
}
}
// 更新 Secret 实例
secretPayload.Type = k8sCore.SecretType(d.config.SecretType)
secretPayload.Type = k8score.SecretType(d.config.SecretType)
if secretPayload.ObjectMeta.Annotations == nil {
secretPayload.ObjectMeta.Annotations = secretAnnotations
} else {
@@ -133,13 +137,12 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
secretPayload.Data[d.config.SecretDataKeyForCrt] = []byte(certPem)
secretPayload.Data[d.config.SecretDataKeyForKey] = []byte(privkeyPem)
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Update(context.TODO(), secretPayload, k8sMeta.UpdateOptions{})
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Update(context.TODO(), secretPayload, k8smeta.UpdateOptions{})
d.logger.Debug("k8s operate 'Secrets.Update'", slog.String("namespace", d.config.Namespace), slog.Any("secret", secretPayload))
if err != nil {
return nil, xerrors.Wrap(err, "failed to update k8s secret")
}
d.logger.Logf("k8s secret updated", secretPayload)
return &deployer.DeployResult{}, nil
}

View File

@@ -1,18 +1,18 @@
package local
package local
import (
"bytes"
"context"
"fmt"
"log/slog"
"os/exec"
"runtime"
xerrors "github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/utils/certs"
"github.com/usual2970/certimate/internal/pkg/utils/files"
"github.com/usual2970/certimate/internal/pkg/utils/certutil"
"github.com/usual2970/certimate/internal/pkg/utils/fileutil"
)
type DeployerConfig struct {
@@ -45,7 +45,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
@@ -57,12 +57,16 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.Default()
} else {
d.logger = logger
}
return d
}
@@ -70,55 +74,48 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 执行前置命令
if d.config.PreCommand != "" {
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PreCommand)
d.logger.Debug("run pre-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
if err != nil {
return nil, xerrors.Wrapf(err, "failed to execute pre-command, stdout: %s, stderr: %s", stdout, stderr)
}
d.logger.Logt("pre-command executed", stdout)
}
// 写入证书和私钥文件
switch d.config.OutputFormat {
case OUTPUT_FORMAT_PEM:
if err := files.WriteString(d.config.OutputCertPath, certPem); err != nil {
if err := fileutil.WriteString(d.config.OutputCertPath, certPem); err != nil {
return nil, xerrors.Wrap(err, "failed to save certificate file")
}
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
d.logger.Logt("certificate file saved")
if err := files.WriteString(d.config.OutputKeyPath, privkeyPem); err != nil {
if err := fileutil.WriteString(d.config.OutputKeyPath, privkeyPem); err != nil {
return nil, xerrors.Wrap(err, "failed to save private key file")
}
d.logger.Logt("private key file saved")
d.logger.Info("ssl private key file saved", slog.String("path", d.config.OutputKeyPath))
case OUTPUT_FORMAT_PFX:
pfxData, err := certs.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword)
pfxData, err := certutil.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword)
if err != nil {
return nil, xerrors.Wrap(err, "failed to transform certificate to PFX")
}
d.logger.Info("ssl certificate transformed to pfx")
d.logger.Logt("certificate transformed to PFX")
if err := files.Write(d.config.OutputCertPath, pfxData); err != nil {
if err := fileutil.Write(d.config.OutputCertPath, pfxData); err != nil {
return nil, xerrors.Wrap(err, "failed to save certificate file")
}
d.logger.Logt("certificate file saved")
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
case OUTPUT_FORMAT_JKS:
jksData, err := certs.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass)
jksData, err := certutil.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass)
if err != nil {
return nil, xerrors.Wrap(err, "failed to transform certificate to JKS")
}
d.logger.Info("ssl certificate transformed to jks")
d.logger.Logt("certificate transformed to JKS")
if err := files.Write(d.config.OutputCertPath, jksData); err != nil {
if err := fileutil.Write(d.config.OutputCertPath, jksData); err != nil {
return nil, xerrors.Wrap(err, "failed to save certificate file")
}
d.logger.Logt("certificate file uploaded")
d.logger.Info("ssl certificate file saved", slog.String("path", d.config.OutputCertPath))
default:
return nil, fmt.Errorf("unsupported output format: %s", d.config.OutputFormat)
@@ -127,11 +124,10 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 执行后置命令
if d.config.PostCommand != "" {
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PostCommand)
d.logger.Debug("run post-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
if err != nil {
return nil, xerrors.Wrapf(err, "failed to execute post-command, stdout: %s, stderr: %s", stdout, stderr)
}
d.logger.Logt("post-command executed", stdout)
}
return &deployer.DeployResult{}, nil

View File

@@ -2,13 +2,13 @@
import (
"context"
"log/slog"
"strings"
xerrors "github.com/pkg/errors"
"github.com/qiniu/go-sdk/v7/auth"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/logger"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/qiniu-sslcert"
qiniusdk "github.com/usual2970/certimate/internal/pkg/vendors/qiniu-sdk"
@@ -25,7 +25,7 @@ type DeployerConfig struct {
type DeployerProvider struct {
config *DeployerConfig
logger logger.Logger
logger *slog.Logger
sdkClient *qiniusdk.Client
sslUploader uploader.Uploader
}
@@ -49,14 +49,19 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
return &DeployerProvider{
config: config,
logger: logger.NewNilLogger(),
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
d.logger = logger
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
}
@@ -65,38 +70,35 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
if err != nil {
return nil, xerrors.Wrap(err, "failed to upload certificate file")
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
d.logger.Logt("certificate file uploaded", upres)
// "*.example.com" → ".example.com",适配七牛云 CDN 要求的泛域名格式
domain := strings.TrimPrefix(d.config.Domain, "*")
// 获取域名信息
// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
getDomainInfoResp, err := d.sdkClient.GetDomainInfo(context.TODO(), domain)
d.logger.Debug("sdk request 'cdn.GetDomainInfo'", slog.String("request.domain", domain), slog.Any("response", getDomainInfoResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDomainInfo'")
}
d.logger.Logt("已获取域名信息", getDomainInfoResp)
// 判断域名是否已启用 HTTPS。如果已启用修改域名证书否则启用 HTTPS
// REF: https://developer.qiniu.com/fusion/4246/the-domain-name
if getDomainInfoResp.Https != nil && getDomainInfoResp.Https.CertID != "" {
modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
}
d.logger.Logt("已修改域名证书", modifyDomainHttpsConfResp)
} else {
if getDomainInfoResp.Https == nil || getDomainInfoResp.Https.CertID == "" {
enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(context.TODO(), domain, upres.CertId, true, true)
d.logger.Debug("sdk request 'cdn.EnableDomainHttps'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", enableDomainHttpsResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.EnableDomainHttps'")
}
d.logger.Logt("已将域名升级为 HTTPS", enableDomainHttpsResp)
} else if getDomainInfoResp.Https.CertID != upres.CertId {
modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(context.TODO(), domain, upres.CertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable)
d.logger.Debug("sdk request 'cdn.ModifyDomainHttpsConf'", slog.String("request.domain", domain), slog.String("request.certId", upres.CertId), slog.Any("response", modifyDomainHttpsConfResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.ModifyDomainHttpsConf'")
}
}
return &deployer.DeployResult{}, nil

View File

@@ -37,7 +37,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_QINIUCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_QINIUCDN_ACCESSKEY="your-access-key" \
--CERTIMATE_DEPLOYER_QINIUCDN_SECRETKEY="your-secret-key" \
--CERTIMATE_DEPLOYER_QINIUCDN_DOMAIN="example.com" \
--CERTIMATE_DEPLOYER_QINIUCDN_DOMAIN="example.com"
*/
func TestDeploy(t *testing.T) {
flag.Parse()

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