mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 01:11:55 +08:00
Merge branch 'main' of https://github.com/usual2970/certimate into usual2970-main
This commit is contained in:
commit
37e2f8110f
@ -30,6 +30,9 @@ builds:
|
|||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: arm
|
goarch: arm
|
||||||
|
|
||||||
|
upx:
|
||||||
|
enable: true
|
||||||
|
|
||||||
release:
|
release:
|
||||||
draft: true
|
draft: true
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ RUN apk add --no-cache tzdata
|
|||||||
COPY ../. /app/
|
COPY ../. /app/
|
||||||
RUN rm -rf /app/ui/dist
|
RUN rm -rf /app/ui/dist
|
||||||
COPY --from=webui-builder /app/ui/dist /app/ui/dist
|
COPY --from=webui-builder /app/ui/dist /app/ui/dist
|
||||||
RUN go build -o certimate
|
ENV CGO_ENABLED=0
|
||||||
|
RUN go build -ldflags="-s -w" -o certimate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
3
Makefile
3
Makefile
@ -21,7 +21,8 @@ $(OS_ARCH):
|
|||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
GOOS=$(word 1,$(subst /, ,$@)) \
|
GOOS=$(word 1,$(subst /, ,$@)) \
|
||||||
GOARCH=$(word 2,$(subst /, ,$@)) \
|
GOARCH=$(word 2,$(subst /, ,$@)) \
|
||||||
go build -o $(BUILD_DIR)/$(BINARY_NAME)_$(word 1,$(subst /, ,$@))_$(word 2,$(subst /, ,$@)) -ldflags="-X main.version=$(VERSION)" .
|
CGO_ENABLED=0 \
|
||||||
|
go build -o $(BUILD_DIR)/$(BINARY_NAME)_$(word 1,$(subst /, ,$@))_$(word 2,$(subst /, ,$@)) -ldflags="-X main.version=$(VERSION) -s -w" .
|
||||||
|
|
||||||
# 清理构建文件
|
# 清理构建文件
|
||||||
clean:
|
clean:
|
||||||
|
@ -49,7 +49,7 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决
|
|||||||
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
|
- 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法;
|
||||||
- 支持 PEM、PFX、JKS 等多种格式输出证书;
|
- 支持 PEM、PFX、JKS 等多种格式输出证书;
|
||||||
- 支持 30+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers));
|
- 支持 30+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers));
|
||||||
- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-host-providers));
|
- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-hosting-providers));
|
||||||
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
|
- 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道;
|
||||||
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
|
- 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构;
|
||||||
- 更多特性等待探索。
|
- 更多特性等待探索。
|
||||||
|
@ -39,7 +39,7 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate
|
|||||||
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
|
- Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC.
|
||||||
- Supports various certificate formats such as PEM, PFX, JKS.
|
- Supports various certificate formats such as PEM, PFX, JKS.
|
||||||
- Supports more than 30+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers));
|
- Supports more than 30+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers));
|
||||||
- Supports more than 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-host-providers));
|
- Supports more than 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-hosting-providers));
|
||||||
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
|
- Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more;
|
||||||
- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust Services,SSL.com, ZeroSSL, and more;
|
- Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust Services,SSL.com, ZeroSSL, and more;
|
||||||
- More features waiting to be discovered.
|
- More features waiting to be discovered.
|
||||||
|
11
go.mod
11
go.mod
@ -20,6 +20,7 @@ require (
|
|||||||
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0
|
github.com/alibabacloud-go/esa-20240910/v2 v2.32.0
|
||||||
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5
|
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5
|
||||||
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12
|
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12
|
||||||
|
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8
|
||||||
github.com/alibabacloud-go/live-20161101 v1.1.1
|
github.com/alibabacloud-go/live-20161101 v1.1.1
|
||||||
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
|
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3
|
||||||
github.com/alibabacloud-go/slb-20140515/v4 v4.0.10
|
github.com/alibabacloud-go/slb-20140515/v4 v4.0.10
|
||||||
@ -30,8 +31,10 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0
|
github.com/aws/aws-sdk-go-v2/service/acm v1.32.0
|
||||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1
|
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1
|
||||||
github.com/baidubce/bce-sdk-go v0.9.226
|
github.com/baidubce/bce-sdk-go v0.9.226
|
||||||
|
github.com/blinkbean/dingtalk v1.1.3
|
||||||
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46
|
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46
|
||||||
github.com/go-acme/lego/v4 v4.23.1
|
github.com/go-acme/lego/v4 v4.23.1
|
||||||
|
github.com/go-lark/lark v1.16.0
|
||||||
github.com/go-resty/resty/v2 v2.16.5
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1
|
github.com/go-viper/mapstructure/v2 v2.2.1
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148
|
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148
|
||||||
@ -39,7 +42,6 @@ require (
|
|||||||
github.com/libdns/dynv6 v1.0.0
|
github.com/libdns/dynv6 v1.0.0
|
||||||
github.com/libdns/libdns v0.2.3
|
github.com/libdns/libdns v0.2.3
|
||||||
github.com/luthermonson/go-proxmox v0.2.2
|
github.com/luthermonson/go-proxmox v0.2.2
|
||||||
github.com/nikoksr/notify v1.3.0
|
|
||||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
|
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
|
||||||
github.com/pkg/sftp v1.13.9
|
github.com/pkg/sftp v1.13.9
|
||||||
github.com/pocketbase/dbx v1.11.0
|
github.com/pocketbase/dbx v1.11.0
|
||||||
@ -84,13 +86,11 @@ require (
|
|||||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
|
||||||
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
github.com/avast/retry-go v3.0.0+incompatible // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect
|
||||||
github.com/blinkbean/dingtalk v1.1.3 // indirect
|
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
github.com/diskfs/go-diskfs v1.5.0 // indirect
|
||||||
github.com/djherbis/times v1.6.0 // indirect
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/go-lark/lark v1.15.1 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
@ -99,7 +99,6 @@ require (
|
|||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-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/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
@ -126,7 +125,6 @@ require (
|
|||||||
github.com/qiniu/x v1.10.5 // indirect
|
github.com/qiniu/x v1.10.5 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
@ -195,12 +193,9 @@ require (
|
|||||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
||||||
github.com/spf13/cast v1.8.0 // indirect
|
github.com/spf13/cast v1.8.0 // indirect
|
||||||
github.com/spf13/cobra v1.9.1 // indirect
|
github.com/spf13/cobra v1.9.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
golang.org/x/image v0.27.0 // indirect
|
golang.org/x/image v0.27.0 // indirect
|
||||||
|
15
go.sum
15
go.sum
@ -112,6 +112,7 @@ 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.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.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.5/go.mod h1:kUe8JqFmoVU7lfBauaDD5taFaW7mBI+xVsyHutYtabg=
|
||||||
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY=
|
||||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 h1:ASXSBga98QrGMxbIThCD6jAti09gedLfvry6yJtsoBE=
|
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 h1:ASXSBga98QrGMxbIThCD6jAti09gedLfvry6yJtsoBE=
|
||||||
@ -137,6 +138,8 @@ github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGL
|
|||||||
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE=
|
github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE=
|
||||||
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM=
|
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM=
|
||||||
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4=
|
github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4=
|
||||||
|
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 h1:5GF0PXijDhxRQ3gTg9Ee/CVPtglkxuVdz4yIQgYLPgw=
|
||||||
|
github.com/alibabacloud-go/ga-20191120/v3 v3.1.8/go.mod h1:RVpR9VL4YECKoZCQijTYfPk8k52O61v6hSRekjxF0kw=
|
||||||
github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8=
|
github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8=
|
||||||
github.com/alibabacloud-go/live-20161101 v1.1.1/go.mod h1:g84w6qeAodT0/IHdc0tEed2a8PyhQhYl7TAj3jGl4A4=
|
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=
|
github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 h1:LtyUVlgBEKyzWgQJurzXM6MXCt84sQr9cE5OKqYymko=
|
||||||
@ -360,8 +363,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
|||||||
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||||
github.com/go-lark/lark v1.15.1 h1:fo6PQKBJht/71N9Zn3/xjknOYx0TmdVuP+VP8NrUCsI=
|
github.com/go-lark/lark v1.16.0 h1:U6BwkLM9wrZedSM7cIiMofganr8PCvJN+M75w2lf2Gg=
|
||||||
github.com/go-lark/lark v1.15.1/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
|
github.com/go-lark/lark v1.16.0/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
@ -400,8 +403,6 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
|
|||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
|
||||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
@ -569,8 +570,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
|
|||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
@ -679,8 +678,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
|
|||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nikoksr/notify v1.3.0 h1:UxzfxzAYGQD9a5JYLBTVx0lFMxeHCke3rPCkfWdPgLs=
|
|
||||||
github.com/nikoksr/notify v1.3.0/go.mod h1:Xor2hMmkvrCfkCKvXGbcrESez4brac2zQjhd6U2BbeM=
|
|
||||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI=
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI=
|
||||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8=
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8=
|
||||||
github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM=
|
github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM=
|
||||||
@ -830,8 +827,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
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/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.1155 h1:ildxJtjnqiKZxWDVKHT/ncIknGDijtg60MuFELON8bY=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155 h1:ildxJtjnqiKZxWDVKHT/ncIknGDijtg60MuFELON8bY=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155/go.mod h1:iLASpooTdyXtx642E5Ws7cfWENsp4/uZ/78TFoln7OI=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155/go.mod h1:iLASpooTdyXtx642E5Ws7cfWENsp4/uZ/78TFoln7OI=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 h1:yGFg9/6j3NP10r9PfSWHfekuq4SwPyqblWnfISfKANo=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 h1:yGFg9/6j3NP10r9PfSWHfekuq4SwPyqblWnfISfKANo=
|
||||||
|
@ -3,25 +3,26 @@ package applicant
|
|||||||
import "github.com/usual2970/certimate/internal/domain"
|
import "github.com/usual2970/certimate/internal/domain"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
|
caLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt)
|
||||||
sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
|
caLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging)
|
||||||
sslProviderBuypass = string(domain.CAProviderTypeBuypass)
|
caBuypass = string(domain.CAProviderTypeBuypass)
|
||||||
sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
|
caGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices)
|
||||||
sslProviderSSLCom = string(domain.CAProviderTypeSSLCom)
|
caSSLCom = string(domain.CAProviderTypeSSLCom)
|
||||||
sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL)
|
caZeroSSL = string(domain.CAProviderTypeZeroSSL)
|
||||||
|
caCustom = string(domain.CAProviderTypeACMECA)
|
||||||
|
|
||||||
sslProviderDefault = sslProviderLetsEncrypt
|
caDefault = caLetsEncrypt
|
||||||
)
|
)
|
||||||
|
|
||||||
var sslProviderUrls = map[string]string{
|
var caDirUrls = map[string]string{
|
||||||
sslProviderLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
|
caLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
sslProviderLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
caLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||||
sslProviderBuypass: "https://api.buypass.com/acme/directory",
|
caBuypass: "https://api.buypass.com/acme/directory",
|
||||||
sslProviderGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory",
|
caGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory",
|
||||||
sslProviderSSLCom: "https://acme.ssl.com/sslcom-dv-rsa",
|
caSSLCom: "https://acme.ssl.com/sslcom-dv-rsa",
|
||||||
sslProviderSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa",
|
caSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa",
|
||||||
sslProviderSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc",
|
caSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc",
|
||||||
sslProviderZeroSSL: "https://acme.zerossl.com/v2/DV90",
|
caZeroSSL: "https://acme.zerossl.com/v2/DV90",
|
||||||
}
|
}
|
||||||
|
|
||||||
type acmeSSLProviderConfig struct {
|
type acmeSSLProviderConfig struct {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
@ -19,22 +20,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type acmeUser struct {
|
type acmeUser struct {
|
||||||
CA string
|
// 证书颁发机构标识。
|
||||||
Email string
|
// 通常等同于 [CAProviderType] 的值。
|
||||||
|
// 对于自定义 ACME CA,值为 "custom#{access_id}"。
|
||||||
|
CA string
|
||||||
|
// 邮箱。
|
||||||
|
Email string
|
||||||
|
// 注册信息。
|
||||||
Registration *registration.Resource
|
Registration *registration.Resource
|
||||||
|
|
||||||
|
// CSR 私钥。
|
||||||
privkey string
|
privkey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAcmeUser(ca, email string) (*acmeUser, error) {
|
func newAcmeUser(ca, caAccessId, email string) (*acmeUser, error) {
|
||||||
repo := repository.NewAcmeAccountRepository()
|
repo := repository.NewAcmeAccountRepository()
|
||||||
|
|
||||||
applyUser := &acmeUser{
|
applyUser := &acmeUser{
|
||||||
CA: ca,
|
CA: ca,
|
||||||
Email: email,
|
Email: email,
|
||||||
}
|
}
|
||||||
|
if ca == caCustom {
|
||||||
|
applyUser.CA = fmt.Sprintf("%s#%s", ca, caAccessId)
|
||||||
|
}
|
||||||
|
|
||||||
acmeAccount, err := repo.GetByCAAndEmail(ca, email)
|
acmeAccount, err := repo.GetByCAAndEmail(applyUser.CA, applyUser.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,6 +83,10 @@ func (u *acmeUser) hasRegistration() bool {
|
|||||||
return u.Registration != nil
|
return u.Registration != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *acmeUser) getCAProvider() string {
|
||||||
|
return strings.Split(u.CA, "#")[0]
|
||||||
|
}
|
||||||
|
|
||||||
func (u *acmeUser) getPrivateKeyPEM() string {
|
func (u *acmeUser) getPrivateKeyPEM() string {
|
||||||
return u.privkey
|
return u.privkey
|
||||||
}
|
}
|
||||||
@ -94,16 +108,16 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userR
|
|||||||
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
|
func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) {
|
||||||
var reg *registration.Resource
|
var reg *registration.Resource
|
||||||
var err error
|
var err error
|
||||||
switch user.CA {
|
switch user.getCAProvider() {
|
||||||
case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging:
|
case caLetsEncrypt, caLetsEncryptStaging:
|
||||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
|
|
||||||
case sslProviderBuypass:
|
case caBuypass:
|
||||||
{
|
{
|
||||||
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
case sslProviderGoogleTrustServices:
|
case caGoogleTrustServices:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForGoogleTrustServices{}
|
access := domain.AccessConfigForGoogleTrustServices{}
|
||||||
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
@ -117,7 +131,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
case sslProviderSSLCom:
|
case caSSLCom:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForSSLCom{}
|
access := domain.AccessConfigForSSLCom{}
|
||||||
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
@ -131,7 +145,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
case sslProviderZeroSSL:
|
case caZeroSSL:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForZeroSSL{}
|
access := domain.AccessConfigForZeroSSL{}
|
||||||
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
@ -145,6 +159,26 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case caCustom:
|
||||||
|
{
|
||||||
|
access := domain.AccessConfigForACMECA{}
|
||||||
|
if err := maputil.Populate(userRegisterOptions, &access); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if access.EabKid == "" && access.EabHmacKey == "" {
|
||||||
|
reg, err = client.Registration.Register(registration.RegisterOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||||||
|
TermsOfServiceAgreed: true,
|
||||||
|
Kid: access.EabKid,
|
||||||
|
HmacEncoded: access.EabHmacKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unsupported ca provider '%s'", user.CA)
|
err = fmt.Errorf("unsupported ca provider '%s'", user.CA)
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,13 @@ import (
|
|||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
|
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
|
||||||
"github.com/usual2970/certimate/internal/repository"
|
"github.com/usual2970/certimate/internal/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApplyResult struct {
|
type ApplyResult struct {
|
||||||
CertificateFullChain string
|
FullChainCertificate string
|
||||||
IssuerCertificate string
|
IssuerCertificate string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
ACMEAccountUrl string
|
ACMEAccountUrl string
|
||||||
@ -53,20 +54,20 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
|
|||||||
|
|
||||||
nodeConfig := config.Node.GetConfigForApply()
|
nodeConfig := config.Node.GetConfigForApply()
|
||||||
options := &applicantProviderOptions{
|
options := &applicantProviderOptions{
|
||||||
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
|
||||||
ContactEmail: nodeConfig.ContactEmail,
|
ContactEmail: nodeConfig.ContactEmail,
|
||||||
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
|
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
|
||||||
ProviderAccessConfig: make(map[string]any),
|
ProviderAccessConfig: make(map[string]any),
|
||||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||||
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
|
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
|
||||||
CAProviderAccessConfig: make(map[string]any),
|
CAProviderAccessConfig: make(map[string]any),
|
||||||
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
|
CAProviderServiceConfig: nodeConfig.CAProviderConfig,
|
||||||
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
||||||
Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
|
Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
|
||||||
DnsPropagationWait: nodeConfig.DnsPropagationWait,
|
DnsPropagationWait: nodeConfig.DnsPropagationWait,
|
||||||
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
|
DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout,
|
||||||
DnsTTL: nodeConfig.DnsTTL,
|
DnsTTL: nodeConfig.DnsTTL,
|
||||||
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
|
DisableFollowCNAME: nodeConfig.DisableFollowCNAME,
|
||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
@ -81,6 +82,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
|
|||||||
if access, err := accessRepo.GetById(context.Background(), nodeConfig.CAProviderAccessId); err != nil {
|
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)
|
return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.CAProviderAccessId, err)
|
||||||
} else {
|
} else {
|
||||||
|
options.CAProviderAccessId = access.Id
|
||||||
options.CAProviderAccessConfig = access.Config
|
options.CAProviderAccessConfig = access.Config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,13 +93,13 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
|
|||||||
|
|
||||||
sslProviderConfig := &acmeSSLProviderConfig{
|
sslProviderConfig := &acmeSSLProviderConfig{
|
||||||
Config: make(map[domain.CAProviderType]map[string]any),
|
Config: make(map[domain.CAProviderType]map[string]any),
|
||||||
Provider: sslProviderDefault,
|
Provider: caDefault,
|
||||||
}
|
}
|
||||||
if settings != nil {
|
if settings != nil {
|
||||||
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
|
if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if sslProviderConfig.Provider == "" {
|
} else if sslProviderConfig.Provider == "" {
|
||||||
sslProviderConfig.Provider = sslProviderDefault
|
sslProviderConfig.Provider = caDefault
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +165,7 @@ func getLimiter(key string) *rate.Limiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
|
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
|
||||||
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
|
user, err := newAcmeUser(string(options.CAProvider), options.CAProviderAccessId, options.ContactEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -175,13 +177,26 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt
|
|||||||
// Create an ACME client config
|
// Create an ACME client config
|
||||||
config := lego.NewConfig(user)
|
config := lego.NewConfig(user)
|
||||||
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
|
||||||
config.CADirURL = sslProviderUrls[user.CA]
|
switch user.getCAProvider() {
|
||||||
if user.CA == sslProviderSSLCom {
|
case caSSLCom:
|
||||||
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
|
if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
|
||||||
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"]
|
config.CADirURL = caDirUrls[caSSLCom+"RSA"]
|
||||||
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
|
} else if strings.HasPrefix(options.KeyAlgorithm, "EC") {
|
||||||
config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"]
|
config.CADirURL = caDirUrls[caSSLCom+"ECC"]
|
||||||
|
} else {
|
||||||
|
config.CADirURL = caDirUrls[caSSLCom]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case caCustom:
|
||||||
|
caDirURL := maputil.GetString(options.CAProviderAccessConfig, "endpoint")
|
||||||
|
if caDirURL != "" {
|
||||||
|
config.CADirURL = caDirURL
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid ca provider endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
config.CADirURL = caDirUrls[user.CA]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an ACME client
|
// Create an ACME client
|
||||||
@ -229,7 +244,7 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ApplyResult{
|
return &ApplyResult{
|
||||||
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
|
FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)),
|
||||||
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
|
||||||
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
|
||||||
ACMEAccountUrl: user.Registration.URI,
|
ACMEAccountUrl: user.Registration.URI,
|
||||||
|
@ -42,22 +42,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type applicantProviderOptions struct {
|
type applicantProviderOptions struct {
|
||||||
Domains []string
|
Domains []string
|
||||||
ContactEmail string
|
ContactEmail string
|
||||||
Provider domain.ACMEDns01ProviderType
|
Provider domain.ACMEDns01ProviderType
|
||||||
ProviderAccessConfig map[string]any
|
ProviderAccessConfig map[string]any
|
||||||
ProviderExtendedConfig map[string]any
|
ProviderServiceConfig map[string]any
|
||||||
CAProvider domain.CAProviderType
|
CAProvider domain.CAProviderType
|
||||||
CAProviderAccessConfig map[string]any
|
CAProviderAccessId string
|
||||||
CAProviderExtendedConfig map[string]any
|
CAProviderAccessConfig map[string]any
|
||||||
KeyAlgorithm string
|
CAProviderServiceConfig map[string]any
|
||||||
Nameservers []string
|
KeyAlgorithm string
|
||||||
DnsPropagationWait int32
|
Nameservers []string
|
||||||
DnsPropagationTimeout int32
|
DnsPropagationWait int32
|
||||||
DnsTTL int32
|
DnsPropagationTimeout int32
|
||||||
DisableFollowCNAME bool
|
DnsTTL int32
|
||||||
ReplacedARIAcct string
|
DisableFollowCNAME bool
|
||||||
ReplacedARICert string
|
ReplacedARIAcct string
|
||||||
|
ReplacedARICert string
|
||||||
}
|
}
|
||||||
|
|
||||||
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
|
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
|
||||||
@ -104,7 +105,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{
|
applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
AccessKeySecret: access.AccessKeySecret,
|
AccessKeySecret: access.AccessKeySecret,
|
||||||
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
@ -125,8 +126,8 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
|
applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
SecretAccessKey: access.SecretAccessKey,
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||||
HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"),
|
HostedZoneId: maputil.GetString(options.ProviderServiceConfig, "hostedZoneId"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
@ -333,7 +334,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
|
applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
SecretAccessKey: access.SecretAccessKey,
|
SecretAccessKey: access.SecretAccessKey,
|
||||||
Region: maputil.GetString(options.ProviderExtendedConfig, "region"),
|
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
@ -350,7 +351,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
|
applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
|
||||||
AccessKeyId: access.AccessKeyId,
|
AccessKeyId: access.AccessKeyId,
|
||||||
AccessKeySecret: access.AccessKeySecret,
|
AccessKeySecret: access.AccessKeySecret,
|
||||||
RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"),
|
RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
@ -520,7 +521,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi
|
|||||||
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
|
applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{
|
||||||
SecretId: access.SecretId,
|
SecretId: access.SecretId,
|
||||||
SecretKey: access.SecretKey,
|
SecretKey: access.SecretKey,
|
||||||
ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"),
|
ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"),
|
||||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||||
DnsTTL: options.DnsTTL,
|
DnsTTL: options.DnsTTL,
|
||||||
})
|
})
|
||||||
|
@ -31,9 +31,9 @@ func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error
|
|||||||
|
|
||||||
nodeConfig := config.Node.GetConfigForDeploy()
|
nodeConfig := config.Node.GetConfigForDeploy()
|
||||||
options := &deployerProviderOptions{
|
options := &deployerProviderOptions{
|
||||||
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
|
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
|
||||||
ProviderAccessConfig: make(map[string]any),
|
ProviderAccessConfig: make(map[string]any),
|
||||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,10 +17,17 @@ type Access struct {
|
|||||||
|
|
||||||
type AccessConfigFor1Panel struct {
|
type AccessConfigFor1Panel struct {
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForACMECA struct {
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
EabKid string `json:"eabKid,omitempty"`
|
||||||
|
EabHmacKey string `json:"eabHmacKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForACMEHttpReq struct {
|
type AccessConfigForACMEHttpReq struct {
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
Mode string `json:"mode,omitempty"`
|
Mode string `json:"mode,omitempty"`
|
||||||
@ -60,6 +67,12 @@ type AccessConfigForBaotaPanel struct {
|
|||||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForBaotaWAF struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForBytePlus struct {
|
type AccessConfigForBytePlus struct {
|
||||||
AccessKey string `json:"accessKey"`
|
AccessKey string `json:"accessKey"`
|
||||||
SecretKey string `json:"secretKey"`
|
SecretKey string `json:"secretKey"`
|
||||||
@ -133,6 +146,14 @@ type AccessConfigForEmail struct {
|
|||||||
DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"`
|
DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForFlexCDN struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiRole string `json:"apiRole"`
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForGcore struct {
|
type AccessConfigForGcore struct {
|
||||||
ApiToken string `json:"apiToken"`
|
ApiToken string `json:"apiToken"`
|
||||||
}
|
}
|
||||||
@ -178,6 +199,15 @@ type AccessConfigForLarkBot struct {
|
|||||||
WebhookUrl string `json:"webhookUrl"`
|
WebhookUrl string `json:"webhookUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForLeCDN struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
|
ApiRole string `json:"apiRole"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForMattermost struct {
|
type AccessConfigForMattermost struct {
|
||||||
ServerUrl string `json:"serverUrl"`
|
ServerUrl string `json:"serverUrl"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@ -240,6 +270,13 @@ type AccessConfigForRainYun struct {
|
|||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccessConfigForRatPanel struct {
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
AccessTokenId int32 `json:"accessTokenId"`
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessConfigForSafeLine struct {
|
type AccessConfigForSafeLine struct {
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
ApiToken string `json:"apiToken"`
|
ApiToken string `json:"apiToken"`
|
||||||
@ -253,6 +290,14 @@ type AccessConfigForSSH struct {
|
|||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
|
JumpServers []struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int32 `json:"port"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
|
} `json:"jumpServers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForSSLCom struct {
|
type AccessConfigForSSLCom struct {
|
||||||
@ -260,7 +305,7 @@ type AccessConfigForSSLCom struct {
|
|||||||
EabHmacKey string `json:"eabHmacKey"`
|
EabHmacKey string `json:"eabHmacKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessConfigForTelegram struct {
|
type AccessConfigForTelegramBot struct {
|
||||||
BotToken string `json:"botToken"`
|
BotToken string `json:"botToken"`
|
||||||
DefaultChatId int64 `json:"defaultChatId,omitempty"`
|
DefaultChatId int64 `json:"defaultChatId,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ type Certificate struct {
|
|||||||
SerialNumber string `json:"serialNumber" db:"serialNumber"`
|
SerialNumber string `json:"serialNumber" db:"serialNumber"`
|
||||||
Certificate string `json:"certificate" db:"certificate"`
|
Certificate string `json:"certificate" db:"certificate"`
|
||||||
PrivateKey string `json:"privateKey" db:"privateKey"`
|
PrivateKey string `json:"privateKey" db:"privateKey"`
|
||||||
Issuer string `json:"issuer" db:"issuer"`
|
IssuerOrg string `json:"issuerOrg" db:"issuerOrg"`
|
||||||
IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"`
|
IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"`
|
||||||
KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"`
|
KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"`
|
||||||
EffectAt time.Time `json:"effectAt" db:"effectAt"`
|
EffectAt time.Time `json:"effectAt" db:"effectAt"`
|
||||||
@ -38,7 +38,7 @@ type Certificate struct {
|
|||||||
func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate {
|
func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate {
|
||||||
c.SubjectAltNames = strings.Join(certX509.DNSNames, ";")
|
c.SubjectAltNames = strings.Join(certX509.DNSNames, ";")
|
||||||
c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16))
|
c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16))
|
||||||
c.Issuer = strings.Join(certX509.Issuer.Organization, ";")
|
c.IssuerOrg = strings.Join(certX509.Issuer.Organization, ";")
|
||||||
c.EffectAt = certX509.NotBefore
|
c.EffectAt = certX509.NotBefore
|
||||||
c.ExpireAt = certX509.NotAfter
|
c.ExpireAt = certX509.NotAfter
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ type AccessProviderType string
|
|||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
AccessProviderType1Panel = AccessProviderType("1panel")
|
AccessProviderType1Panel = AccessProviderType("1panel")
|
||||||
AccessProviderTypeACMECA = AccessProviderType("acmeca") // ACME CA(预留)
|
AccessProviderTypeACMECA = AccessProviderType("acmeca")
|
||||||
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
||||||
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
||||||
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
||||||
@ -19,6 +19,7 @@ const (
|
|||||||
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
|
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
|
||||||
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
AccessProviderTypeBaishan = AccessProviderType("baishan")
|
||||||
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
|
||||||
|
AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf")
|
||||||
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
AccessProviderTypeBytePlus = AccessProviderType("byteplus")
|
||||||
AccessProviderTypeBunny = AccessProviderType("bunny")
|
AccessProviderTypeBunny = AccessProviderType("bunny")
|
||||||
AccessProviderTypeBuypass = AccessProviderType("buypass")
|
AccessProviderTypeBuypass = AccessProviderType("buypass")
|
||||||
@ -36,8 +37,8 @@ const (
|
|||||||
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
||||||
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
||||||
AccessProviderTypeEmail = AccessProviderType("email")
|
AccessProviderTypeEmail = AccessProviderType("email")
|
||||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||||
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") // FlexCDN(预留)
|
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn")
|
||||||
AccessProviderTypeGname = AccessProviderType("gname")
|
AccessProviderTypeGname = AccessProviderType("gname")
|
||||||
AccessProviderTypeGcore = AccessProviderType("gcore")
|
AccessProviderTypeGcore = AccessProviderType("gcore")
|
||||||
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
|
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
|
||||||
@ -49,7 +50,7 @@ const (
|
|||||||
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
|
||||||
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
|
||||||
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
|
AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging")
|
||||||
AccessProviderTypeLeCDN = AccessProviderType("lecdn") // LeCDN(预留)
|
AccessProviderTypeLeCDN = AccessProviderType("lecdn")
|
||||||
AccessProviderTypeLocal = AccessProviderType("local")
|
AccessProviderTypeLocal = AccessProviderType("local")
|
||||||
AccessProviderTypeMattermost = AccessProviderType("mattermost")
|
AccessProviderTypeMattermost = AccessProviderType("mattermost")
|
||||||
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
||||||
@ -64,10 +65,11 @@ const (
|
|||||||
AccessProviderTypeQiniu = AccessProviderType("qiniu")
|
AccessProviderTypeQiniu = AccessProviderType("qiniu")
|
||||||
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
|
AccessProviderTypeQingCloud = AccessProviderType("qingcloud") // 青云(预留)
|
||||||
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
AccessProviderTypeRainYun = AccessProviderType("rainyun")
|
||||||
|
AccessProviderTypeRatPanel = AccessProviderType("ratpanel")
|
||||||
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
AccessProviderTypeSafeLine = AccessProviderType("safeline")
|
||||||
AccessProviderTypeSSH = AccessProviderType("ssh")
|
AccessProviderTypeSSH = AccessProviderType("ssh")
|
||||||
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
|
AccessProviderTypeSSLCOM = AccessProviderType("sslcom")
|
||||||
AccessProviderTypeTelegram = AccessProviderType("telegram")
|
AccessProviderTypeTelegramBot = AccessProviderType("telegrambot")
|
||||||
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud")
|
||||||
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
AccessProviderTypeUCloud = AccessProviderType("ucloud")
|
||||||
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
||||||
@ -90,6 +92,7 @@ type CAProviderType string
|
|||||||
NOTICE: If you add new constant, please keep ASCII order.
|
NOTICE: If you add new constant, please keep ASCII order.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
|
CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA)
|
||||||
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
|
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
|
||||||
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
|
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
|
||||||
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
|
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
|
||||||
@ -172,7 +175,7 @@ const (
|
|||||||
DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos")
|
DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos")
|
||||||
DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa")
|
DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa")
|
||||||
DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc")
|
DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc")
|
||||||
DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga") // 阿里云全球加速(预留)
|
DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga")
|
||||||
DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live")
|
DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live")
|
||||||
DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb")
|
DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb")
|
||||||
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
|
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
|
||||||
@ -188,13 +191,15 @@ const (
|
|||||||
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
|
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
|
||||||
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
|
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
|
||||||
DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site")
|
DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site")
|
||||||
|
DeploymentProviderTypeBaotaWAFConsole = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-console")
|
||||||
|
DeploymentProviderTypeBaotaWAFSite = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-site")
|
||||||
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
|
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
|
||||||
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
|
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
|
||||||
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
|
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
|
||||||
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
|
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
|
||||||
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
|
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
|
||||||
DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications")
|
DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications")
|
||||||
DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) // FlexCDN(预留)
|
DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN)
|
||||||
DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn")
|
DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn")
|
||||||
DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge)
|
DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge)
|
||||||
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
|
DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn")
|
||||||
@ -206,7 +211,7 @@ const (
|
|||||||
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live")
|
||||||
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod")
|
||||||
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret")
|
||||||
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) // LeCDN(预留)
|
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN)
|
||||||
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
|
||||||
DeploymentProviderTypeNetlifySite = DeploymentProviderType(AccessProviderTypeNetlify + "-site")
|
DeploymentProviderTypeNetlifySite = DeploymentProviderType(AccessProviderTypeNetlify + "-site")
|
||||||
DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE)
|
DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE)
|
||||||
@ -214,6 +219,8 @@ const (
|
|||||||
DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo")
|
DeploymentProviderTypeQiniuKodo = DeploymentProviderType(AccessProviderTypeQiniu + "-kodo")
|
||||||
DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili")
|
DeploymentProviderTypeQiniuPili = DeploymentProviderType(AccessProviderTypeQiniu + "-pili")
|
||||||
DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn")
|
DeploymentProviderTypeRainYunRCDN = DeploymentProviderType(AccessProviderTypeRainYun + "-rcdn")
|
||||||
|
DeploymentProviderTypeRatPanelConsole = DeploymentProviderType(AccessProviderTypeRatPanel + "-console")
|
||||||
|
DeploymentProviderTypeRatPanelSite = DeploymentProviderType(AccessProviderTypeRatPanel + "-site")
|
||||||
DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine)
|
DeploymentProviderTypeSafeLine = DeploymentProviderType(AccessProviderTypeSafeLine)
|
||||||
DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH)
|
DeploymentProviderTypeSSH = DeploymentProviderType(AccessProviderTypeSSH)
|
||||||
DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn")
|
DeploymentProviderTypeTencentCloudCDN = DeploymentProviderType(AccessProviderTypeTencentCloud + "-cdn")
|
||||||
@ -239,9 +246,9 @@ const (
|
|||||||
DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex")
|
DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex")
|
||||||
DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live")
|
DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live")
|
||||||
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
|
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
|
||||||
DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn") // 网宿 CDN(预留)
|
DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn")
|
||||||
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
|
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
|
||||||
DeploymentProviderTypeWangsuCert = DeploymentProviderType(AccessProviderTypeWangsu + "-cert") // 网宿证书管理(预留)
|
DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate")
|
||||||
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
|
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -259,7 +266,7 @@ const (
|
|||||||
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
|
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
|
||||||
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
|
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
|
||||||
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
|
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
|
||||||
NotificationProviderTypeTelegram = NotificationProviderType(AccessProviderTypeTelegram)
|
NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot)
|
||||||
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
|
NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook)
|
||||||
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
|
NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot)
|
||||||
)
|
)
|
||||||
|
@ -105,11 +105,6 @@ type WorkflowNodeConfigForNotify struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
||||||
skipBeforeExpiryDays := maputil.GetInt32(n.Config, "skipBeforeExpiryDays")
|
|
||||||
if skipBeforeExpiryDays == 0 {
|
|
||||||
skipBeforeExpiryDays = 30
|
|
||||||
}
|
|
||||||
|
|
||||||
return WorkflowNodeConfigForApply{
|
return WorkflowNodeConfigForApply{
|
||||||
Domains: maputil.GetString(n.Config, "domains"),
|
Domains: maputil.GetString(n.Config, "domains"),
|
||||||
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
|
ContactEmail: maputil.GetString(n.Config, "contactEmail"),
|
||||||
@ -126,7 +121,7 @@ func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
|
|||||||
DnsTTL: maputil.GetInt32(n.Config, "dnsTTL"),
|
DnsTTL: maputil.GetInt32(n.Config, "dnsTTL"),
|
||||||
DisableFollowCNAME: maputil.GetBool(n.Config, "disableFollowCNAME"),
|
DisableFollowCNAME: maputil.GetBool(n.Config, "disableFollowCNAME"),
|
||||||
DisableARI: maputil.GetBool(n.Config, "disableARI"),
|
DisableARI: maputil.GetBool(n.Config, "disableARI"),
|
||||||
SkipBeforeExpiryDays: skipBeforeExpiryDays,
|
SkipBeforeExpiryDays: maputil.GetOrDefaultInt32(n.Config, "skipBeforeExpiryDays", 30),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@ func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error
|
|||||||
|
|
||||||
nodeConfig := config.Node.GetConfigForNotify()
|
nodeConfig := config.Node.GetConfigForNotify()
|
||||||
options := ¬ifierProviderOptions{
|
options := ¬ifierProviderOptions{
|
||||||
Provider: domain.NotificationProviderType(nodeConfig.Provider),
|
Provider: domain.NotificationProviderType(nodeConfig.Provider),
|
||||||
ProviderAccessConfig: make(map[string]any),
|
ProviderAccessConfig: make(map[string]any),
|
||||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
|
@ -6,21 +6,21 @@ import (
|
|||||||
|
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||||
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||||
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
|
httputil "github.com/usual2970/certimate/internal/pkg/utils/http"
|
||||||
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
type notifierProviderOptions struct {
|
type notifierProviderOptions struct {
|
||||||
Provider domain.NotificationProviderType
|
Provider domain.NotificationProviderType
|
||||||
ProviderAccessConfig map[string]any
|
ProviderAccessConfig map[string]any
|
||||||
ProviderExtendedConfig map[string]any
|
ProviderServiceConfig map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) {
|
func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) {
|
||||||
@ -36,7 +36,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pDingTalk.NewNotifier(&pDingTalk.NotifierConfig{
|
return pDingTalkBot.NewNotifier(&pDingTalkBot.NotifierConfig{
|
||||||
WebhookUrl: access.WebhookUrl,
|
WebhookUrl: access.WebhookUrl,
|
||||||
Secret: access.Secret,
|
Secret: access.Secret,
|
||||||
})
|
})
|
||||||
@ -55,8 +55,8 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
SmtpTls: access.SmtpTls,
|
SmtpTls: access.SmtpTls,
|
||||||
Username: access.Username,
|
Username: access.Username,
|
||||||
Password: access.Password,
|
Password: access.Password,
|
||||||
SenderAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "senderAddress", access.DefaultSenderAddress),
|
SenderAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "senderAddress", access.DefaultSenderAddress),
|
||||||
ReceiverAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "receiverAddress", access.DefaultReceiverAddress),
|
ReceiverAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.DefaultReceiverAddress),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pLark.NewNotifier(&pLark.NotifierConfig{
|
return pLarkBot.NewNotifier(&pLarkBot.NotifierConfig{
|
||||||
WebhookUrl: access.WebhookUrl,
|
WebhookUrl: access.WebhookUrl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -83,20 +83,20 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
ServerUrl: access.ServerUrl,
|
ServerUrl: access.ServerUrl,
|
||||||
Username: access.Username,
|
Username: access.Username,
|
||||||
Password: access.Password,
|
Password: access.Password,
|
||||||
ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId),
|
ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
case domain.NotificationProviderTypeTelegram:
|
case domain.NotificationProviderTypeTelegramBot:
|
||||||
{
|
{
|
||||||
access := domain.AccessConfigForTelegram{}
|
access := domain.AccessConfigForTelegramBot{}
|
||||||
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
|
||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pTelegram.NewNotifier(&pTelegram.NotifierConfig{
|
return pTelegramBot.NewNotifier(&pTelegramBot.NotifierConfig{
|
||||||
BotToken: access.BotToken,
|
BotToken: access.BotToken,
|
||||||
ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId),
|
ChatId: maputil.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.DefaultChatId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" {
|
if extendedHeadersString := maputil.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" {
|
||||||
h, err := httputil.ParseHeaders(extendedHeadersString)
|
h, err := httputil.ParseHeaders(extendedHeadersString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
return nil, fmt.Errorf("failed to parse webhook headers: %w", err)
|
||||||
@ -129,7 +129,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
|
|
||||||
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
|
||||||
WebhookUrl: access.Url,
|
WebhookUrl: access.Url,
|
||||||
WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification),
|
WebhookData: maputil.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DefaultDataForNotification),
|
||||||
Method: access.Method,
|
Method: access.Method,
|
||||||
Headers: mergedHeaders,
|
Headers: mergedHeaders,
|
||||||
AllowInsecureConnections: access.AllowInsecureConnections,
|
AllowInsecureConnections: access.AllowInsecureConnections,
|
||||||
@ -143,7 +143,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
|
|||||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pWeCom.NewNotifier(&pWeCom.NotifierConfig{
|
return pWeComBot.NewNotifier(&pWeComBot.NotifierConfig{
|
||||||
WebhookUrl: access.WebhookUrl,
|
WebhookUrl: access.WebhookUrl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,17 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
||||||
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||||
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
||||||
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||||
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||||
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
||||||
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
||||||
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
||||||
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||||
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||||
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, c
|
|||||||
|
|
||||||
case domain.NotifyChannelTypeGotify:
|
case domain.NotifyChannelTypeGotify:
|
||||||
return pGotify.NewNotifier(&pGotify.NotifierConfig{
|
return pGotify.NewNotifier(&pGotify.NotifierConfig{
|
||||||
Url: maputil.GetString(channelConfig, "url"),
|
ServerUrl: maputil.GetString(channelConfig, "url"),
|
||||||
Token: maputil.GetString(channelConfig, "token"),
|
Token: maputil.GetString(channelConfig, "token"),
|
||||||
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
|
Priority: maputil.GetOrDefaultInt64(channelConfig, "priority", 1),
|
||||||
})
|
})
|
||||||
|
|
||||||
case domain.NotifyChannelTypeLark:
|
case domain.NotifyChannelTypeLark:
|
||||||
@ -83,7 +83,7 @@ func createNotifierProviderUseGlobalSettings(channel domain.NotifyChannelType, c
|
|||||||
|
|
||||||
case domain.NotifyChannelTypeServerChan:
|
case domain.NotifyChannelTypeServerChan:
|
||||||
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
return pServerChan.NewNotifier(&pServerChan.NotifierConfig{
|
||||||
Url: maputil.GetString(channelConfig, "url"),
|
ServerUrl: maputil.GetString(channelConfig, "url"),
|
||||||
})
|
})
|
||||||
|
|
||||||
case domain.NotifyChannelTypeTelegram:
|
case domain.NotifyChannelTypeTelegram:
|
||||||
|
@ -24,6 +24,7 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider,
|
|||||||
providerConfig := internal.NewDefaultConfig()
|
providerConfig := internal.NewDefaultConfig()
|
||||||
providerConfig.SecretID = config.AccessKeyId
|
providerConfig.SecretID = config.AccessKeyId
|
||||||
providerConfig.SecretKey = config.AccessKeySecret
|
providerConfig.SecretKey = config.AccessKeySecret
|
||||||
|
providerConfig.RegionID = config.Region
|
||||||
if config.DnsPropagationTimeout != 0 {
|
if config.DnsPropagationTimeout != 0 {
|
||||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
client: client,
|
client: client,
|
||||||
config: config,
|
config: config,
|
||||||
|
|
||||||
|
siteIDs: make(map[string]int64),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +102,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
siteId, err := d.getSiteId(authZone)
|
siteId, err := d.getSiteId(strings.TrimRight(authZone, "."))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
||||||
}
|
}
|
||||||
@ -120,7 +122,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
siteId, err := d.getSiteId(authZone)
|
siteId, err := d.getSiteId(strings.TrimRight(authZone, "."))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err)
|
||||||
}
|
}
|
||||||
@ -148,10 +150,11 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
|||||||
pageSize := 500
|
pageSize := 500
|
||||||
for {
|
for {
|
||||||
request := &aliesa.ListSitesRequest{
|
request := &aliesa.ListSitesRequest{
|
||||||
SiteName: tea.String(siteName),
|
SiteName: tea.String(siteName),
|
||||||
PageNumber: tea.Int32(int32(pageNumber)),
|
SiteSearchType: tea.String("exact"),
|
||||||
PageSize: tea.Int32(int32(pageNumber)),
|
PageNumber: tea.Int32(int32(pageNumber)),
|
||||||
AccessType: tea.String("NS"),
|
PageSize: tea.Int32(int32(pageSize)),
|
||||||
|
AccessType: tea.String("NS"),
|
||||||
}
|
}
|
||||||
response, err := d.client.ListSites(request)
|
response, err := d.client.ListSites(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -178,7 +181,7 @@ func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, errors.New("failed to get site id")
|
return 0, errors.New("site not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) {
|
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) {
|
||||||
@ -186,11 +189,12 @@ func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa
|
|||||||
pageSize := 500
|
pageSize := 500
|
||||||
for {
|
for {
|
||||||
request := &aliesa.ListRecordsRequest{
|
request := &aliesa.ListRecordsRequest{
|
||||||
SiteId: tea.Int64(siteId),
|
SiteId: tea.Int64(siteId),
|
||||||
Type: tea.String("TXT"),
|
Type: tea.String("TXT"),
|
||||||
RecordName: tea.String(effectiveFQDN),
|
RecordName: tea.String(effectiveFQDN),
|
||||||
PageNumber: tea.Int32(int32(pageNumber)),
|
RecordMatchType: tea.String("exact"),
|
||||||
PageSize: tea.Int32(int32(pageNumber)),
|
PageNumber: tea.Int32(int32(pageNumber)),
|
||||||
|
PageSize: tea.Int32(int32(pageSize)),
|
||||||
}
|
}
|
||||||
response, err := d.client.ListRecords(request)
|
response, err := d.client.ListRecords(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9,12 +9,15 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
// 1Panel 地址。
|
// 1Panel 地址。
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 1Panel 版本。
|
||||||
|
// 可取值 "v1"、"v2"。
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
// 1Panel 接口密钥。
|
// 1Panel 接口密钥。
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
// 是否允许不安全的连接。
|
// 是否允许不安全的连接。
|
||||||
@ -26,7 +29,7 @@ type DeployerConfig struct {
|
|||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sdkClient *opsdk.Client
|
sdkClient *onepanelsdk.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
@ -36,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
@ -59,7 +62,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
|||||||
|
|
||||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
// 设置面板 SSL 证书
|
// 设置面板 SSL 证书
|
||||||
updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
|
updateSystemSSLReq := &onepanelsdk.UpdateSystemSSLRequest{
|
||||||
Cert: certPEM,
|
Cert: certPEM,
|
||||||
Key: privkeyPEM,
|
Key: privkeyPEM,
|
||||||
SSL: "enable",
|
SSL: "enable",
|
||||||
@ -79,16 +82,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
|
func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
|
||||||
if _, err := url.Parse(apiUrl); err != nil {
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
return nil, errors.New("invalid 1panel api url")
|
return nil, errors.New("invalid 1panel api url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apiVersion == "" {
|
||||||
|
return nil, errors.New("invalid 1panel api version")
|
||||||
|
}
|
||||||
|
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
return nil, errors.New("invalid 1panel api key")
|
return nil, errors.New("invalid 1panel api key")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := opsdk.NewClient(apiUrl, apiKey)
|
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
|
||||||
if skipTlsVerify {
|
if skipTlsVerify {
|
||||||
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
fInputCertPath string
|
fInputCertPath string
|
||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fApiUrl string
|
fApiUrl string
|
||||||
|
fApiVersion string
|
||||||
fApiKey string
|
fApiKey string
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
|
||||||
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +36,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \
|
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \
|
||||||
|
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIVERSION="v1" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key"
|
--CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
@ -45,11 +48,13 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
fmt.Sprintf("APIURL: %v", fApiUrl),
|
fmt.Sprintf("APIURL: %v", fApiUrl),
|
||||||
|
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
ApiUrl: fApiUrl,
|
ApiUrl: fApiUrl,
|
||||||
|
ApiVersion: fApiVersion,
|
||||||
ApiKey: fApiKey,
|
ApiKey: fApiKey,
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
AutoRestart: true,
|
AutoRestart: true,
|
||||||
|
@ -12,12 +12,15 @@ import (
|
|||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
|
||||||
opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
// 1Panel 地址。
|
// 1Panel 地址。
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 1Panel 版本。
|
||||||
|
// 可取值 "v1"、"v2"。
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
// 1Panel 接口密钥。
|
// 1Panel 接口密钥。
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
// 是否允许不安全的连接。
|
// 是否允许不安全的连接。
|
||||||
@ -35,7 +38,7 @@ type DeployerConfig struct {
|
|||||||
type DeployerProvider struct {
|
type DeployerProvider struct {
|
||||||
config *DeployerConfig
|
config *DeployerConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sdkClient *opsdk.Client
|
sdkClient *onepanelsdk.Client
|
||||||
sslUploader uploader.Uploader
|
sslUploader uploader.Uploader
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,14 +49,15 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
ApiUrl: config.ApiUrl,
|
ApiUrl: config.ApiUrl,
|
||||||
ApiKey: config.ApiKey,
|
ApiVersion: config.ApiVersion,
|
||||||
|
ApiKey: config.ApiKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
@ -103,7 +107,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取网站 HTTPS 配置
|
// 获取网站 HTTPS 配置
|
||||||
getHttpsConfReq := &opsdk.GetHttpsConfRequest{
|
getHttpsConfReq := &onepanelsdk.GetHttpsConfRequest{
|
||||||
WebsiteID: d.config.WebsiteId,
|
WebsiteID: d.config.WebsiteId,
|
||||||
}
|
}
|
||||||
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
|
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
|
||||||
@ -122,7 +126,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
|
|||||||
|
|
||||||
// 修改网站 HTTPS 配置
|
// 修改网站 HTTPS 配置
|
||||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||||
updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
|
updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{
|
||||||
WebsiteID: d.config.WebsiteId,
|
WebsiteID: d.config.WebsiteId,
|
||||||
Type: "existed",
|
Type: "existed",
|
||||||
WebsiteSSLID: certId,
|
WebsiteSSLID: certId,
|
||||||
@ -147,7 +151,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取证书详情
|
// 获取证书详情
|
||||||
getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{
|
getWebsiteSSLReq := &onepanelsdk.GetWebsiteSSLRequest{
|
||||||
SSLID: d.config.CertificateId,
|
SSLID: d.config.CertificateId,
|
||||||
}
|
}
|
||||||
getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq)
|
getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq)
|
||||||
@ -157,7 +161,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新证书
|
// 更新证书
|
||||||
uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
|
uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
|
||||||
Type: "paste",
|
Type: "paste",
|
||||||
SSLID: d.config.CertificateId,
|
SSLID: d.config.CertificateId,
|
||||||
Description: getWebsiteSSLResp.Data.Description,
|
Description: getWebsiteSSLResp.Data.Description,
|
||||||
@ -173,16 +177,20 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
|
func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
|
||||||
if _, err := url.Parse(apiUrl); err != nil {
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
return nil, errors.New("invalid 1panel api url")
|
return nil, errors.New("invalid 1panel api url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apiVersion == "" {
|
||||||
|
return nil, errors.New("invalid 1panel api version")
|
||||||
|
}
|
||||||
|
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
return nil, errors.New("invalid 1panel api key")
|
return nil, errors.New("invalid 1panel api key")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := opsdk.NewClient(apiUrl, apiKey)
|
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
|
||||||
if skipTlsVerify {
|
if skipTlsVerify {
|
||||||
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
fInputCertPath string
|
fInputCertPath string
|
||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fApiUrl string
|
fApiUrl string
|
||||||
|
fApiVersion string
|
||||||
fApiKey string
|
fApiKey string
|
||||||
fWebsiteId int64
|
fWebsiteId int64
|
||||||
)
|
)
|
||||||
@ -25,6 +26,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
|
||||||
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||||
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
|
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
|
||||||
}
|
}
|
||||||
@ -36,6 +38,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \
|
||||||
|
--CERTIMATE_DEPLOYER_1PANELSITE_APIVERSION="v1" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \
|
--CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \
|
||||||
--CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
|
--CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
|
||||||
*/
|
*/
|
||||||
@ -48,12 +51,14 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
fmt.Sprintf("APIURL: %v", fApiUrl),
|
fmt.Sprintf("APIURL: %v", fApiUrl),
|
||||||
|
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||||
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
|
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
ApiUrl: fApiUrl,
|
ApiUrl: fApiUrl,
|
||||||
|
ApiVersion: fApiVersion,
|
||||||
ApiKey: fApiKey,
|
ApiKey: fApiKey,
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
|
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
|
||||||
|
@ -157,7 +157,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
for _, listener := range listListenersResp.Body.Listeners {
|
for _, listener := range listListenersResp.Body.Listeners {
|
||||||
listenerIds = append(listenerIds, *listener.ListenerId)
|
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
for _, listener := range listListenersResp.Body.Listeners {
|
for _, listener := range listListenersResp.Body.Listeners {
|
||||||
listenerIds = append(listenerIds, *listener.ListenerId)
|
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,8 +211,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
|
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||||
|
|
||||||
for _, listenerId := range listenerIds {
|
for _, listenerId := range listenerIds {
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
select {
|
||||||
errs = append(errs, err)
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
fmt.Sprintf("REGION: %v", fRegion),
|
fmt.Sprintf("REGION: %v", fRegion),
|
||||||
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
@ -171,7 +171,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
@ -18,7 +18,7 @@ var (
|
|||||||
fAccessKeySecret string
|
fAccessKeySecret string
|
||||||
fRegion string
|
fRegion string
|
||||||
fLoadbalancerId string
|
fLoadbalancerId string
|
||||||
fListenerPort int
|
fListenerPort int64
|
||||||
fDomain string
|
fDomain string
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ func init() {
|
|||||||
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||||
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
||||||
flag.IntVar(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
|
flag.Int64Var(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "")
|
||||||
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ type DeployerConfig struct {
|
|||||||
// 阿里云地域。
|
// 阿里云地域。
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
// 服务版本。
|
// 服务版本。
|
||||||
|
// 可取值 "2.0"、"3.0"。
|
||||||
ServiceVersion string `json:"serviceVersion"`
|
ServiceVersion string `json:"serviceVersion"`
|
||||||
// 自定义域名(支持泛域名)。
|
// 自定义域名(支持泛域名)。
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
|
322
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
Normal file
322
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
package aliyunga
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||||
|
aliga "github.com/alibabacloud-go/ga-20191120/v3/client"
|
||||||
|
"github.com/alibabacloud-go/tea/tea"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas"
|
||||||
|
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 阿里云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 阿里云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
|
// 全球加速实例 ID。
|
||||||
|
AcceleratorId string `json:"acceleratorId"`
|
||||||
|
// 全球加速监听 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
|
||||||
|
ListenerId string `json:"listenerId,omitempty"`
|
||||||
|
// SNI 域名(不支持泛域名)。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_ACCELERATOR]、[RESOURCE_TYPE_LISTENER] 时选填。
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *aliga.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
d.sslUploader.WithLogger(logger)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到 CAS
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case RESOURCE_TYPE_ACCELERATOR:
|
||||||
|
if err := d.deployToAccelerator(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
case RESOURCE_TYPE_LISTENER:
|
||||||
|
if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToAccelerator(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.AcceleratorId == "" {
|
||||||
|
return errors.New("config `acceleratorId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 HTTPS 监听列表
|
||||||
|
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlisteners
|
||||||
|
listenerIds := make([]string, 0)
|
||||||
|
listListenersPageNumber := int32(1)
|
||||||
|
listListenersPageSize := int32(50)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
listListenersReq := &aliga.ListListenersRequest{
|
||||||
|
RegionId: tea.String("cn-hangzhou"),
|
||||||
|
AcceleratorId: tea.String(d.config.AcceleratorId),
|
||||||
|
PageNumber: tea.Int32(listListenersPageNumber),
|
||||||
|
PageSize: tea.Int32(listListenersPageSize),
|
||||||
|
}
|
||||||
|
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
|
||||||
|
d.logger.Debug("sdk request 'ga.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ga.ListListeners': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenersResp.Body.Listeners != nil {
|
||||||
|
for _, listener := range listListenersResp.Body.Listeners {
|
||||||
|
if strings.EqualFold(tea.StringValue(listener.Protocol), "https") {
|
||||||
|
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(listListenersResp.Body.Listeners) < int(listListenersPageSize) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenersPageNumber++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历更新监听证书
|
||||||
|
if len(listenerIds) == 0 {
|
||||||
|
d.logger.Info("no ga listeners to deploy")
|
||||||
|
} else {
|
||||||
|
var errs []error
|
||||||
|
d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||||
|
|
||||||
|
for _, listenerId := range listenerIds {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, listenerId, cloudCertId); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
|
||||||
|
if d.config.AcceleratorId == "" {
|
||||||
|
return errors.New("config `acceleratorId` is required")
|
||||||
|
}
|
||||||
|
if d.config.ListenerId == "" {
|
||||||
|
return errors.New("config `listenerId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新监听
|
||||||
|
if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, d.config.ListenerId, cloudCertId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudAcceleratorId string, cloudListenerId string, cloudCertId string) error {
|
||||||
|
// 查询监听绑定的证书列表
|
||||||
|
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlistenercertificates
|
||||||
|
var listenerDefaultCertificate *aliga.ListListenerCertificatesResponseBodyCertificates
|
||||||
|
var listenerAdditionalCertificates []*aliga.ListListenerCertificatesResponseBodyCertificates = make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||||
|
var listListenerCertificatesNextToken *string
|
||||||
|
for {
|
||||||
|
listListenerCertificatesReq := &aliga.ListListenerCertificatesRequest{
|
||||||
|
RegionId: tea.String("cn-hangzhou"),
|
||||||
|
AcceleratorId: tea.String(d.config.AcceleratorId),
|
||||||
|
NextToken: listListenerCertificatesNextToken,
|
||||||
|
MaxResults: tea.Int32(20),
|
||||||
|
}
|
||||||
|
listListenerCertificatesResp, err := d.sdkClient.ListListenerCertificates(listListenerCertificatesReq)
|
||||||
|
d.logger.Debug("sdk request 'ga.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ga.ListListenerCertificates': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenerCertificatesResp.Body.Certificates != nil {
|
||||||
|
for _, certificate := range listListenerCertificatesResp.Body.Certificates {
|
||||||
|
if tea.BoolValue(certificate.IsDefault) {
|
||||||
|
listenerDefaultCertificate = certificate
|
||||||
|
} else {
|
||||||
|
listenerAdditionalCertificates = append(listenerAdditionalCertificates, certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if listListenerCertificatesResp.Body.NextToken == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.config.Domain == "" {
|
||||||
|
// 未指定 SNI,只需部署到监听器
|
||||||
|
if listenerDefaultCertificate != nil && tea.StringValue(listenerDefaultCertificate.CertificateId) == cloudCertId {
|
||||||
|
d.logger.Info("no need to update ga listener default certificate")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改监听的属性
|
||||||
|
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updatelistener
|
||||||
|
updateListenerReq := &aliga.UpdateListenerRequest{
|
||||||
|
RegionId: tea.String("cn-hangzhou"),
|
||||||
|
ListenerId: tea.String(cloudListenerId),
|
||||||
|
Certificates: []*aliga.UpdateListenerRequestCertificates{{
|
||||||
|
Id: tea.String(cloudCertId),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq)
|
||||||
|
d.logger.Debug("sdk request 'ga.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ga.UpdateListener': %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 指定 SNI,需部署到扩展域名
|
||||||
|
if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
|
||||||
|
return tea.StringValue(item.CertificateId) == cloudCertId
|
||||||
|
}) {
|
||||||
|
d.logger.Info("no need to update ga listener additional certificate")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool {
|
||||||
|
return tea.StringValue(item.Domain) == d.config.Domain
|
||||||
|
}) {
|
||||||
|
// 为监听替换扩展证书
|
||||||
|
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updateadditionalcertificatewithlistener
|
||||||
|
updateAdditionalCertificateWithListenerReq := &aliga.UpdateAdditionalCertificateWithListenerRequest{
|
||||||
|
RegionId: tea.String("cn-hangzhou"),
|
||||||
|
AcceleratorId: tea.String(cloudAcceleratorId),
|
||||||
|
ListenerId: tea.String(cloudListenerId),
|
||||||
|
CertificateId: tea.String(cloudCertId),
|
||||||
|
Domain: tea.String(d.config.Domain),
|
||||||
|
}
|
||||||
|
updateAdditionalCertificateWithListenerResp, err := d.sdkClient.UpdateAdditionalCertificateWithListener(updateAdditionalCertificateWithListenerReq)
|
||||||
|
d.logger.Debug("sdk request 'ga.UpdateAdditionalCertificateWithListener'", slog.Any("request", updateAdditionalCertificateWithListenerReq), slog.Any("response", updateAdditionalCertificateWithListenerResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ga.UpdateAdditionalCertificateWithListener': %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 为监听绑定扩展证书
|
||||||
|
// REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-associateadditionalcertificateswithlistener
|
||||||
|
associateAdditionalCertificatesWithListenerReq := &aliga.AssociateAdditionalCertificatesWithListenerRequest{
|
||||||
|
RegionId: tea.String("cn-hangzhou"),
|
||||||
|
AcceleratorId: tea.String(cloudAcceleratorId),
|
||||||
|
ListenerId: tea.String(cloudListenerId),
|
||||||
|
Certificates: []*aliga.AssociateAdditionalCertificatesWithListenerRequestCertificates{{
|
||||||
|
Id: tea.String(cloudCertId),
|
||||||
|
Domain: tea.String(d.config.Domain),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
associateAdditionalCertificatesWithListenerResp, err := d.sdkClient.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesWithListenerReq)
|
||||||
|
d.logger.Debug("sdk request 'ga.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesWithListenerReq), slog.Any("response", associateAdditionalCertificatesWithListenerResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ga.AssociateAdditionalCertificatesWithListener': %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*aliga.Client, error) {
|
||||||
|
// 接入点一览 https://api.aliyun.com/product/Ga
|
||||||
|
config := &aliopen.Config{
|
||||||
|
AccessKeyId: tea.String(accessKeyId),
|
||||||
|
AccessKeySecret: tea.String(accessKeySecret),
|
||||||
|
Endpoint: tea.String("ga.cn-hangzhou.aliyuncs.com"),
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := aliga.NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSslUploader(accessKeyId, accessKeySecret string) (uploader.Uploader, error) {
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: accessKeyId,
|
||||||
|
AccessKeySecret: accessKeySecret,
|
||||||
|
Region: "cn-hangzhou",
|
||||||
|
})
|
||||||
|
return uploader, err
|
||||||
|
}
|
118
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
Normal file
118
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package aliyunga_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fAcceleratorId string
|
||||||
|
fListenerId string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNGA_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fAcceleratorId, argsPrefix+"ACCELERATORID", "", "")
|
||||||
|
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./aliyun_ga_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_ACCELERATORID="your-ga-accelerator-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_LISTENERID="your-ga-listener-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_ALIYUNGA_DOMAIN="your-ga-sni-domain"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToAccelerator", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("ACCELERATORID: %v", fAcceleratorId),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_ACCELERATOR,
|
||||||
|
AcceleratorId: fAcceleratorId,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deploy_ToListener", func(t *testing.T) {
|
||||||
|
t.Log(strings.Join([]string{
|
||||||
|
"args:",
|
||||||
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
|
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
||||||
|
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_LISTENER,
|
||||||
|
ListenerId: fListenerId,
|
||||||
|
Domain: fDomain,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("ok: %v", res)
|
||||||
|
})
|
||||||
|
}
|
10
internal/pkg/core/deployer/providers/aliyun-ga/consts.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-ga/consts.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package aliyunga
|
||||||
|
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:部署到指定全球加速器。
|
||||||
|
RESOURCE_TYPE_ACCELERATOR = ResourceType("accelerator")
|
||||||
|
// 资源类型:部署到指定监听器。
|
||||||
|
RESOURCE_TYPE_LISTENER = ResourceType("listener")
|
||||||
|
)
|
@ -145,7 +145,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
|
|
||||||
if listListenersResp.Body.Listeners != nil {
|
if listListenersResp.Body.Listeners != nil {
|
||||||
for _, listener := range listListenersResp.Body.Listeners {
|
for _, listener := range listListenersResp.Body.Listeners {
|
||||||
listenerIds = append(listenerIds, *listener.ListenerId)
|
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package baotapanelconsole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 堡塔云 WAF 地址。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 堡塔云 WAF 接口密钥。
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *btsdk.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, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
configSetSSLReq := &btsdk.ConfigSetSSLRequest{
|
||||||
|
CertContent: certPEM,
|
||||||
|
KeyContent: privkeyPEM,
|
||||||
|
}
|
||||||
|
configSetSSLResp, err := d.sdkClient.ConfigSetSSL(configSetSSLReq)
|
||||||
|
d.logger.Debug("sdk request 'bt.ConfigSetSSL'", slog.Any("request", configSetSSLReq), slog.Any("response", configSetSSLResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'bt.ConfigSetSSL': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid baota api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiKey == "" {
|
||||||
|
return nil, errors.New("invalid baota api key")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := btsdk.NewClient(apiUrl, apiKey)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package baotapanelconsole_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-console"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fApiKey string
|
||||||
|
fSiteName string
|
||||||
|
fSitePort int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_"
|
||||||
|
|
||||||
|
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 ./baotawaf_console_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIURL="http://127.0.0.1:8888" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_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,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
package baotapanelwaf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf"
|
||||||
|
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 堡塔云 WAF 地址。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 堡塔云 WAF 接口密钥。
|
||||||
|
ApiKey string `json:"apiKey"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 网站名称。
|
||||||
|
SiteName string `json:"siteName"`
|
||||||
|
// 网站 SSL 端口。
|
||||||
|
// 零值时默认为 443。
|
||||||
|
SitePort int32 `json:"sitePort,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *btsdk.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, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if d.config.SiteName == "" {
|
||||||
|
return nil, errors.New("config `siteName` is required")
|
||||||
|
}
|
||||||
|
if d.config.SitePort == 0 {
|
||||||
|
d.config.SitePort = 443
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历获取网站列表,获取网站 ID
|
||||||
|
// REF: https://support.huaweicloud.com/api-waf/ListHost.html
|
||||||
|
siteId := ""
|
||||||
|
getSitListPage := int32(1)
|
||||||
|
getSitListPageSize := int32(100)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
getSiteListReq := &btsdk.GetSiteListRequest{
|
||||||
|
SiteName: typeutil.ToPtr(d.config.SiteName),
|
||||||
|
Page: typeutil.ToPtr(getSitListPage),
|
||||||
|
PageSize: typeutil.ToPtr(getSitListPageSize),
|
||||||
|
}
|
||||||
|
getSiteListResp, err := d.sdkClient.GetSiteList(getSiteListReq)
|
||||||
|
d.logger.Debug("sdk request 'bt.GetSiteList'", slog.Any("request", getSiteListReq), slog.Any("response", getSiteListResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'bt.GetSiteList': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getSiteListResp.Result != nil && getSiteListResp.Result.List != nil {
|
||||||
|
for _, siteItem := range getSiteListResp.Result.List {
|
||||||
|
if siteItem.SiteName == d.config.SiteName {
|
||||||
|
siteId = siteItem.SiteId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if getSiteListResp.Result == nil || len(getSiteListResp.Result.List) < int(getSitListPageSize) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
getSitListPage++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if siteId == "" {
|
||||||
|
return nil, errors.New("site not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改站点配置
|
||||||
|
modifySiteReq := &btsdk.ModifySiteRequest{
|
||||||
|
SiteId: siteId,
|
||||||
|
Type: typeutil.ToPtr("openCert"),
|
||||||
|
Server: &btsdk.SiteServerInfo{
|
||||||
|
ListenSSLPort: typeutil.ToPtr(d.config.SitePort),
|
||||||
|
SSL: &btsdk.SiteServerSSLInfo{
|
||||||
|
IsSSL: typeutil.ToPtr(int32(1)),
|
||||||
|
FullChain: typeutil.ToPtr(certPEM),
|
||||||
|
PrivateKey: typeutil.ToPtr(privkeyPEM),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
modifySiteResp, err := d.sdkClient.ModifySite(modifySiteReq)
|
||||||
|
d.logger.Debug("sdk request 'bt.ModifySite'", slog.Any("request", modifySiteReq), slog.Any("response", modifySiteResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'bt.ModifySite': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid baota api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiKey == "" {
|
||||||
|
return nil, errors.New("invalid baota api key")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := btsdk.NewClient(apiUrl, apiKey)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package baotapanelwaf_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-site"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fApiKey string
|
||||||
|
fSiteName string
|
||||||
|
fSitePort int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFSITE_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||||
|
flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "")
|
||||||
|
flag.Int64Var(&fSitePort, argsPrefix+"SITEPORT", 0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./baotawaf_site_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIURL="http://127.0.0.1:8888" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIKEY="your-api-key" \
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITENAME="your-site-name"\
|
||||||
|
--CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITEPORT=443
|
||||||
|
*/
|
||||||
|
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("SITENAME: %v", fSiteName),
|
||||||
|
fmt.Sprintf("SITEPORT: %v", fSitePort),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
ApiUrl: fApiUrl,
|
||||||
|
ApiKey: fApiKey,
|
||||||
|
AllowInsecureConnections: true,
|
||||||
|
SiteName: fSiteName,
|
||||||
|
SitePort: int32(fSitePort),
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
8
internal/pkg/core/deployer/providers/flexcdn/consts.go
Normal file
8
internal/pkg/core/deployer/providers/flexcdn/consts.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package flexcdn
|
||||||
|
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:替换指定证书。
|
||||||
|
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||||
|
)
|
145
internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
Normal file
145
internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package flexcdn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
flexcdnsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/flexcdn"
|
||||||
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// FlexCDN URL。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// FlexCDN 用户角色。
|
||||||
|
// 可取值 "user"、"admin"。
|
||||||
|
ApiRole string `json:"apiRole"`
|
||||||
|
// FlexCDN AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// FlexCDN AccessKey。
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
|
// 证书 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||||
|
CertificateId int64 `json:"certificateId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *flexcdnsdk.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.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case RESOURCE_TYPE_CERTIFICATE:
|
||||||
|
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.CertificateId == 0 {
|
||||||
|
return errors.New("config `certificateId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析证书内容
|
||||||
|
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改证书
|
||||||
|
// REF: https://flexcdn.cloud/dev/api/service/SSLCertService?role=user#updateSSLCert
|
||||||
|
updateSSLCertReq := &flexcdnsdk.UpdateSSLCertRequest{
|
||||||
|
SSLCertId: d.config.CertificateId,
|
||||||
|
IsOn: true,
|
||||||
|
Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
|
||||||
|
Description: "upload from certimate",
|
||||||
|
ServerName: certX509.Subject.CommonName,
|
||||||
|
IsCA: false,
|
||||||
|
CertData: base64.StdEncoding.EncodeToString([]byte(certPEM)),
|
||||||
|
KeyData: base64.StdEncoding.EncodeToString([]byte(privkeyPEM)),
|
||||||
|
TimeBeginAt: certX509.NotBefore.Unix(),
|
||||||
|
TimeEndAt: certX509.NotAfter.Unix(),
|
||||||
|
DNSNames: certX509.DNSNames,
|
||||||
|
CommonNames: []string{certX509.Subject.CommonName},
|
||||||
|
}
|
||||||
|
updateSSLCertResp, err := d.sdkClient.UpdateSSLCert(updateSSLCertReq)
|
||||||
|
d.logger.Debug("sdk request 'flexcdn.UpdateSSLCert'", slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'flexcdn.UpdateSSLCert': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid flexcdn api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiRole != "user" && apiRole != "admin" {
|
||||||
|
return nil, errors.New("invalid flexcdn api role")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessKeyId == "" {
|
||||||
|
return nil, errors.New("invalid flexcdn access key id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessKey == "" {
|
||||||
|
return nil, errors.New("invalid flexcdn access key")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := flexcdnsdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
83
internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
Normal file
83
internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package flexcdn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKey string
|
||||||
|
fCertificateId int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_FLEXCDN_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
|
||||||
|
flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./flexcdn_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_APIURL="http://127.0.0.1:7788" \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \
|
||||||
|
--CERTIMATE_DEPLOYER_FLEXCDN_CERTIFICATEID="your-cerficiate-id"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToCertificate", 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("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEY: %v", fAccessKey),
|
||||||
|
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
ApiUrl: fApiUrl,
|
||||||
|
ApiRole: "user",
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKey: fAccessKey,
|
||||||
|
AllowInsecureConnections: true,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
||||||
|
CertificateId: fCertificateId,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -19,6 +19,7 @@ type DeployerConfig struct {
|
|||||||
// GoEdge URL。
|
// GoEdge URL。
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
// GoEdge 用户角色。
|
// GoEdge 用户角色。
|
||||||
|
// 可取值 "user"、"admin"。
|
||||||
ApiRole string `json:"apiRole"`
|
ApiRole string `json:"apiRole"`
|
||||||
// GoEdge AccessKeyId。
|
// GoEdge AccessKeyId。
|
||||||
AccessKeyId string `json:"accessKeyId"`
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
@ -17,7 +17,7 @@ var (
|
|||||||
fApiUrl string
|
fApiUrl string
|
||||||
fAccessKeyId string
|
fAccessKeyId string
|
||||||
fAccessKey string
|
fAccessKey string
|
||||||
fCertificateId int
|
fCertificateId int64
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -28,7 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
|
flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "")
|
||||||
flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -45,7 +45,7 @@ Shell command to run this test:
|
|||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
t.Run("Deploy", func(t *testing.T) {
|
t.Run("Deploy_ToCertificate", func(t *testing.T) {
|
||||||
t.Log(strings.Join([]string{
|
t.Log(strings.Join([]string{
|
||||||
"args:",
|
"args:",
|
||||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
@ -58,11 +58,12 @@ func TestDeploy(t *testing.T) {
|
|||||||
|
|
||||||
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
ApiUrl: fApiUrl,
|
ApiUrl: fApiUrl,
|
||||||
|
ApiRole: "user",
|
||||||
AccessKeyId: fAccessKeyId,
|
AccessKeyId: fAccessKeyId,
|
||||||
AccessKey: fAccessKey,
|
AccessKey: fAccessKey,
|
||||||
AllowInsecureConnections: true,
|
AllowInsecureConnections: true,
|
||||||
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
||||||
CertificateId: int64(fCertificateId),
|
CertificateId: fCertificateId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
@ -210,7 +210,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil {
|
if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
8
internal/pkg/core/deployer/providers/lecdn/consts.go
Normal file
8
internal/pkg/core/deployer/providers/lecdn/consts.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package lecdn
|
||||||
|
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 资源类型:替换指定证书。
|
||||||
|
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||||
|
)
|
176
internal/pkg/core/deployer/providers/lecdn/lecdn.go
Normal file
176
internal/pkg/core/deployer/providers/lecdn/lecdn.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package lecdn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
leclientsdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/client"
|
||||||
|
lemastersdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/master"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// LeCDN URL。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// LeCDN 版本。
|
||||||
|
// 可取值 "v3"。
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
|
// LeCDN 用户角色。
|
||||||
|
// 可取值 "client"、"master"。
|
||||||
|
ApiRole string `json:"apiRole"`
|
||||||
|
// LeCDN 用户名。
|
||||||
|
Username string `json:"accessKeyId"`
|
||||||
|
// LeCDN 用户密码。
|
||||||
|
Password string `json:"accessKey"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 部署资源类型。
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
|
// 证书 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||||
|
CertificateId int64 `json:"certificateId,omitempty"`
|
||||||
|
// 客户 ID。
|
||||||
|
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时选填。
|
||||||
|
ClientId int64 `json:"clientId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
const (
|
||||||
|
apiVersionV3 = "v3"
|
||||||
|
|
||||||
|
apiRoleClient = "client"
|
||||||
|
apiRoleMaster = "master"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 根据部署资源类型决定部署方式
|
||||||
|
switch d.config.ResourceType {
|
||||||
|
case RESOURCE_TYPE_CERTIFICATE:
|
||||||
|
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||||
|
if d.config.CertificateId == 0 {
|
||||||
|
return errors.New("config `certificateId` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改证书
|
||||||
|
// REF: https://wdk0pwf8ul.feishu.cn/wiki/YE1XwCRIHiLYeKkPupgcXrlgnDd
|
||||||
|
switch sdkClient := d.sdkClient.(type) {
|
||||||
|
case *leclientsdkv3.Client:
|
||||||
|
updateSSLCertReq := &leclientsdkv3.UpdateCertificateRequest{
|
||||||
|
Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
|
||||||
|
Description: "upload from certimate",
|
||||||
|
Type: "upload",
|
||||||
|
SSLPEM: certPEM,
|
||||||
|
SSLKey: privkeyPEM,
|
||||||
|
AutoRenewal: false,
|
||||||
|
}
|
||||||
|
updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq)
|
||||||
|
d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *lemastersdkv3.Client:
|
||||||
|
updateSSLCertReq := &lemastersdkv3.UpdateCertificateRequest{
|
||||||
|
ClientId: d.config.ClientId,
|
||||||
|
Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()),
|
||||||
|
Description: "upload from certimate",
|
||||||
|
Type: "upload",
|
||||||
|
SSLPEM: certPEM,
|
||||||
|
SSLKey: privkeyPEM,
|
||||||
|
AutoRenewal: false,
|
||||||
|
}
|
||||||
|
updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq)
|
||||||
|
d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("sdk client is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid lecdn api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" {
|
||||||
|
return nil, errors.New("invalid lecdn username")
|
||||||
|
}
|
||||||
|
|
||||||
|
if password == "" {
|
||||||
|
return nil, errors.New("invalid lecdn password")
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiVersion == apiVersionV3 && apiRole == apiRoleClient {
|
||||||
|
// v3 版客户端
|
||||||
|
client := leclientsdkv3.NewClient(apiUrl, username, password)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
} else if apiVersion == apiVersionV3 && apiRole == apiRoleMaster {
|
||||||
|
// v3 版主控端
|
||||||
|
client := lemastersdkv3.NewClient(apiUrl, username, password)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("invalid lecdn api version or user role")
|
||||||
|
}
|
87
internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
Normal file
87
internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package lecdn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/lecdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fApiVersion string
|
||||||
|
fUsername string
|
||||||
|
fPassword string
|
||||||
|
fCertificateId int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_LECDN_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v3", "")
|
||||||
|
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
|
||||||
|
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
|
||||||
|
flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./lecdn_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_APIURL="http://127.0.0.1:5090" \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_USERNAME="your-username" \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_PASSWORD="your-password" \
|
||||||
|
--CERTIMATE_DEPLOYER_LECDN_CERTIFICATEID="your-cerficiate-id"
|
||||||
|
*/
|
||||||
|
func TestDeploy(t *testing.T) {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
t.Run("Deploy_ToCertificate", 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("APIVERSION: %v", fApiVersion),
|
||||||
|
fmt.Sprintf("USERNAME: %v", fUsername),
|
||||||
|
fmt.Sprintf("PASSWORD: %v", fPassword),
|
||||||
|
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
ApiUrl: fApiUrl,
|
||||||
|
ApiVersion: fApiVersion,
|
||||||
|
ApiRole: "user",
|
||||||
|
Username: fUsername,
|
||||||
|
Password: fPassword,
|
||||||
|
AllowInsecureConnections: true,
|
||||||
|
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
||||||
|
CertificateId: fCertificateId,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package ratpanelconsole
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
rpsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/ratpanel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 耗子面板地址。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 耗子面板访问令牌 ID。
|
||||||
|
AccessTokenId int32 `json:"accessTokenId"`
|
||||||
|
// 耗子面板访问令牌。
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *rpsdk.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.AccessTokenId, config.AccessToken, config.AllowInsecureConnections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 证书
|
||||||
|
settingCertReq := &rpsdk.SettingCertRequest{
|
||||||
|
Certificate: certPEM,
|
||||||
|
PrivateKey: privkeyPEM,
|
||||||
|
}
|
||||||
|
settingCertResp, err := d.sdkClient.SettingCert(settingCertReq)
|
||||||
|
d.logger.Debug("sdk request 'ratpanel.SettingCert'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCert': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid ratpanel api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessTokenId == 0 {
|
||||||
|
return nil, errors.New("invalid ratpanel access token id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessToken == "" {
|
||||||
|
return nil, errors.New("invalid ratpanel access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package ratpanelconsole_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-console"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fAccessTokenId int64
|
||||||
|
fAccessToken string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_RATPANELCONSOLE_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "")
|
||||||
|
flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./ratpanel_console_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELCONSOLE_APIURL="http://127.0.0.1:8888" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKENID="your-access-token-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKEN="your-access-token"
|
||||||
|
*/
|
||||||
|
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("ACCESSTOKENID: %v", fAccessTokenId),
|
||||||
|
fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
ApiUrl: fApiUrl,
|
||||||
|
AccessTokenId: int32(fAccessTokenId),
|
||||||
|
AccessToken: fAccessToken,
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package ratpanelsite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
|
rpsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/ratpanel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 耗子面板地址。
|
||||||
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 耗子面板访问令牌 ID。
|
||||||
|
AccessTokenId int32 `json:"accessTokenId"`
|
||||||
|
// 耗子面板访问令牌。
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
// 是否允许不安全的连接。
|
||||||
|
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||||
|
// 网站名称。
|
||||||
|
SiteName string `json:"siteName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *rpsdk.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.AccessTokenId, config.AccessToken, config.AllowInsecureConnections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if d.config.SiteName == "" {
|
||||||
|
return nil, errors.New("config `siteName` is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置站点 SSL 证书
|
||||||
|
websiteCertReq := &rpsdk.WebsiteCertRequest{
|
||||||
|
SiteName: d.config.SiteName,
|
||||||
|
Certificate: certPEM,
|
||||||
|
PrivateKey: privkeyPEM,
|
||||||
|
}
|
||||||
|
websiteCertResp, err := d.sdkClient.WebsiteCert(websiteCertReq)
|
||||||
|
d.logger.Debug("sdk request 'ratpanel.WebsiteCert'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCert': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||||
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
|
return nil, errors.New("invalid ratpanel api url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessTokenId == 0 {
|
||||||
|
return nil, errors.New("invalid ratpanel access token id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessToken == "" {
|
||||||
|
return nil, errors.New("invalid ratpanel access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken)
|
||||||
|
if skipTlsVerify {
|
||||||
|
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package ratpanelsite_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ratpanel-site"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fApiUrl string
|
||||||
|
fAccessTokenId int64
|
||||||
|
fAccessToken string
|
||||||
|
fSiteName string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_RATPANELSITE_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "")
|
||||||
|
flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
|
||||||
|
flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./ratpanel_site_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_APIURL="http://127.0.0.1:8888" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKENID="your-access-token-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKEN="your-access-token" \
|
||||||
|
--CERTIMATE_DEPLOYER_RATPANELSITE_SITENAME="your-site-name"
|
||||||
|
*/
|
||||||
|
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("ACCESSTOKENID: %v", fAccessTokenId),
|
||||||
|
fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
|
||||||
|
fmt.Sprintf("SITENAME: %v", fSiteName),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
ApiUrl: fApiUrl,
|
||||||
|
AccessTokenId: int32(fAccessTokenId),
|
||||||
|
AccessToken: fAccessToken,
|
||||||
|
AllowInsecureConnections: true,
|
||||||
|
SiteName: fSiteName,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -16,7 +16,7 @@ var (
|
|||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fApiUrl string
|
fApiUrl string
|
||||||
fApiToken string
|
fApiToken string
|
||||||
fCertificateId int
|
fCertificateId int64
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -26,7 +26,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||||
flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -16,6 +17,23 @@ import (
|
|||||||
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type JumpServerConfig struct {
|
||||||
|
// SSH 主机。
|
||||||
|
// 零值时默认为 "localhost"。
|
||||||
|
SshHost string `json:"sshHost,omitempty"`
|
||||||
|
// SSH 端口。
|
||||||
|
// 零值时默认为 22。
|
||||||
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
|
// SSH 登录用户名。
|
||||||
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
|
// SSH 登录密码。
|
||||||
|
SshPassword string `json:"sshPassword,omitempty"`
|
||||||
|
// SSH 登录私钥。
|
||||||
|
SshKey string `json:"sshKey,omitempty"`
|
||||||
|
// SSH 登录私钥口令。
|
||||||
|
SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeployerConfig struct {
|
type DeployerConfig struct {
|
||||||
// SSH 主机。
|
// SSH 主机。
|
||||||
// 零值时默认为 "localhost"。
|
// 零值时默认为 "localhost"。
|
||||||
@ -31,6 +49,8 @@ type DeployerConfig struct {
|
|||||||
SshKey string `json:"sshKey,omitempty"`
|
SshKey string `json:"sshKey,omitempty"`
|
||||||
// SSH 登录私钥口令。
|
// SSH 登录私钥口令。
|
||||||
SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
|
SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
|
||||||
|
// 跳板机配置数组。
|
||||||
|
JumpServers []JumpServerConfig `json:"jumpServers,omitempty"`
|
||||||
// 是否回退使用 SCP。
|
// 是否回退使用 SCP。
|
||||||
UseSCP bool `json:"useSCP,omitempty"`
|
UseSCP bool `json:"useSCP,omitempty"`
|
||||||
// 前置命令。
|
// 前置命令。
|
||||||
@ -97,8 +117,61 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return nil, fmt.Errorf("failed to extract certs: %w", err)
|
return nil, fmt.Errorf("failed to extract certs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接
|
var targetConn net.Conn
|
||||||
|
|
||||||
|
// 连接到跳板机
|
||||||
|
if len(d.config.JumpServers) > 0 {
|
||||||
|
var jumpClient *ssh.Client
|
||||||
|
for i, jumpServerConf := range d.config.JumpServers {
|
||||||
|
d.logger.Info(fmt.Sprintf("connecting to jump server [%d]", i+1), slog.String("host", jumpServerConf.SshHost))
|
||||||
|
|
||||||
|
var jumpConn net.Conn
|
||||||
|
// 第一个连接是主机发起,后续通过跳板机发起
|
||||||
|
if jumpClient == nil {
|
||||||
|
jumpConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", jumpServerConf.SshHost, jumpServerConf.SshPort))
|
||||||
|
} else {
|
||||||
|
jumpConn, err = jumpClient.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", jumpServerConf.SshHost, jumpServerConf.SshPort))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to jump server [%d]: %w", i+1, err)
|
||||||
|
}
|
||||||
|
defer jumpConn.Close()
|
||||||
|
|
||||||
|
newClient, err := createSshClient(
|
||||||
|
jumpConn,
|
||||||
|
jumpServerConf.SshHost,
|
||||||
|
jumpServerConf.SshPort,
|
||||||
|
jumpServerConf.SshUsername,
|
||||||
|
jumpServerConf.SshPassword,
|
||||||
|
jumpServerConf.SshKey,
|
||||||
|
jumpServerConf.SshKeyPassphrase,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create jump server ssh client[%d]: %w", i+1, err)
|
||||||
|
}
|
||||||
|
defer newClient.Close()
|
||||||
|
|
||||||
|
jumpClient = newClient
|
||||||
|
d.logger.Info(fmt.Sprintf("jump server connected [%d]", i+1), slog.String("host", jumpServerConf.SshHost))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过跳板机发起 TCP 连接到目标服务器
|
||||||
|
targetConn, err = jumpClient.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", d.config.SshHost, d.config.SshPort))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to target server: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直接发起 TCP 连接到目标服务器
|
||||||
|
targetConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", d.config.SshHost, d.config.SshPort))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to target server: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer targetConn.Close()
|
||||||
|
|
||||||
|
// 通过已有的连接创建目标服务器 SSH 客户端
|
||||||
client, err := createSshClient(
|
client, err := createSshClient(
|
||||||
|
targetConn,
|
||||||
d.config.SshHost,
|
d.config.SshHost,
|
||||||
d.config.SshPort,
|
d.config.SshPort,
|
||||||
d.config.SshUsername,
|
d.config.SshUsername,
|
||||||
@ -189,7 +262,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSshClient(host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
|
func createSshClient(conn net.Conn, host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
}
|
}
|
||||||
@ -217,11 +290,16 @@ func createSshClient(host string, port int32, username string, password string,
|
|||||||
authMethod = ssh.Password(password)
|
authMethod = ssh.Password(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
||||||
User: username,
|
User: username,
|
||||||
Auth: []ssh.AuthMethod{authMethod},
|
Auth: []ssh.AuthMethod{authMethod},
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssh.NewClient(sshConn, chans, reqs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) {
|
func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) {
|
||||||
|
@ -15,7 +15,7 @@ var (
|
|||||||
fInputCertPath string
|
fInputCertPath string
|
||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fSshHost string
|
fSshHost string
|
||||||
fSshPort int
|
fSshPort int64
|
||||||
fSshUsername string
|
fSshUsername string
|
||||||
fSshPassword string
|
fSshPassword string
|
||||||
fOutputCertPath string
|
fOutputCertPath string
|
||||||
@ -28,7 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fSshHost, argsPrefix+"SSHHOST", "", "")
|
flag.StringVar(&fSshHost, argsPrefix+"SSHHOST", "", "")
|
||||||
flag.IntVar(&fSshPort, argsPrefix+"SSHPORT", 0, "")
|
flag.Int64Var(&fSshPort, argsPrefix+"SSHPORT", 0, "")
|
||||||
flag.StringVar(&fSshUsername, argsPrefix+"SSHUSERNAME", "", "")
|
flag.StringVar(&fSshUsername, argsPrefix+"SSHUSERNAME", "", "")
|
||||||
flag.StringVar(&fSshPassword, argsPrefix+"SSHPASSWORD", "", "")
|
flag.StringVar(&fSshPassword, argsPrefix+"SSHPASSWORD", "", "")
|
||||||
flag.StringVar(&fOutputCertPath, argsPrefix+"OUTPUTCERTPATH", "", "")
|
flag.StringVar(&fOutputCertPath, argsPrefix+"OUTPUTCERTPATH", "", "")
|
||||||
|
@ -2,9 +2,11 @@ package tencentcloudcdn
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
@ -132,6 +134,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
|
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
|
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
||||||
|
describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var runningCount, succeededCount, failedCount, totalCount int64
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
||||||
|
return nil, errors.New("unexpected deployment job status")
|
||||||
|
} else {
|
||||||
|
if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
|
||||||
|
runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
||||||
|
succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
||||||
|
failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
|
||||||
|
totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if succeededCount+failedCount == totalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
tcclb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317"
|
tcclb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb/v20180317"
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
@ -151,6 +152,49 @@ func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId
|
|||||||
return fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
return fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
|
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
|
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
||||||
|
describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var runningCount, succeededCount, failedCount, totalCount int64
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
||||||
|
return errors.New("unexpected deployment job status")
|
||||||
|
} else {
|
||||||
|
if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
|
||||||
|
runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
||||||
|
succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
||||||
|
failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
|
||||||
|
totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if succeededCount+failedCount == totalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||||
@ -102,6 +103,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
|
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
|
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
||||||
|
describeHostDeployRecordDetailResp, err := d.sdkClient.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var runningCount, succeededCount, failedCount, totalCount int64
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
||||||
|
return nil, errors.New("unexpected deployment job status")
|
||||||
|
} else {
|
||||||
|
if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
|
||||||
|
runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
||||||
|
succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
||||||
|
failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
|
||||||
|
totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if succeededCount+failedCount == totalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ package tencentcloudecdn
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
tccdn "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn/v20180606"
|
||||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
@ -103,7 +105,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
} else {
|
} else {
|
||||||
d.logger.Info("found ecdn instances to deploy", slog.Any("instanceIds", instanceIds))
|
d.logger.Info("found ecdn instances to deploy", slog.Any("instanceIds", instanceIds))
|
||||||
|
|
||||||
// 证书部署到 ECDN 实例
|
// 证书部署到 CDN 实例
|
||||||
// REF: https://cloud.tencent.com/document/product/400/91667
|
// REF: https://cloud.tencent.com/document/product/400/91667
|
||||||
deployCertificateInstanceReq := tcssl.NewDeployCertificateInstanceRequest()
|
deployCertificateInstanceReq := tcssl.NewDeployCertificateInstanceRequest()
|
||||||
deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
|
deployCertificateInstanceReq.CertificateId = common.StringPtr(upres.CertId)
|
||||||
@ -115,6 +117,49 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DeployCertificateInstance': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 循环获取部署任务详情,等待任务状态变更
|
||||||
|
// REF: https://cloud.tencent.com.cn/document/api/400/91658
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
|
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
||||||
|
describeHostDeployRecordDetailResp, err := d.sdkClients.SSL.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
||||||
|
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var runningCount, succeededCount, failedCount, totalCount int64
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
||||||
|
return nil, errors.New("unexpected deployment job status")
|
||||||
|
} else {
|
||||||
|
if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
|
||||||
|
runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
||||||
|
succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
||||||
|
failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
|
||||||
|
totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
if succeededCount+failedCount == totalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
|
@ -116,30 +116,35 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
|
|
||||||
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
describeHostDeployRecordDetailReq := tcssl.NewDescribeHostDeployRecordDetailRequest()
|
||||||
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
describeHostDeployRecordDetailReq.DeployRecordId = common.StringPtr(fmt.Sprintf("%d", *deployCertificateInstanceResp.Response.DeployRecordId))
|
||||||
describeHostDeployRecordDetailReq.Limit = common.Uint64Ptr(100)
|
|
||||||
describeHostDeployRecordDetailResp, err := d.sdkClient.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
describeHostDeployRecordDetailResp, err := d.sdkClient.DescribeHostDeployRecordDetail(describeHostDeployRecordDetailReq)
|
||||||
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
d.logger.Debug("sdk request 'ssl.DescribeHostDeployRecordDetail'", slog.Any("request", describeHostDeployRecordDetailReq), slog.Any("response", describeHostDeployRecordDetailResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeHostDeployRecordDetail': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var runningCount, succeededCount, failedCount, totalCount int64
|
||||||
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
if describeHostDeployRecordDetailResp.Response.TotalCount == nil {
|
||||||
return nil, errors.New("unexpected deployment job status")
|
return nil, errors.New("unexpected deployment job status")
|
||||||
} else {
|
} else {
|
||||||
acc := int64(0)
|
if describeHostDeployRecordDetailResp.Response.RunningTotalCount != nil {
|
||||||
|
runningCount = *describeHostDeployRecordDetailResp.Response.RunningTotalCount
|
||||||
|
}
|
||||||
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
if describeHostDeployRecordDetailResp.Response.SuccessTotalCount != nil {
|
||||||
acc += *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
succeededCount = *describeHostDeployRecordDetailResp.Response.SuccessTotalCount
|
||||||
}
|
}
|
||||||
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
if describeHostDeployRecordDetailResp.Response.FailedTotalCount != nil {
|
||||||
acc += *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
failedCount = *describeHostDeployRecordDetailResp.Response.FailedTotalCount
|
||||||
|
}
|
||||||
|
if describeHostDeployRecordDetailResp.Response.TotalCount != nil {
|
||||||
|
totalCount = *describeHostDeployRecordDetailResp.Response.TotalCount
|
||||||
}
|
}
|
||||||
|
|
||||||
if acc == *describeHostDeployRecordDetailResp.Response.TotalCount {
|
if succeededCount+failedCount == totalCount {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Info("waiting for deployment job completion ...")
|
d.logger.Info(fmt.Sprintf("waiting for deployment job completion (running: %d, succeeded: %d, failed: %d, total: %d) ...", runningCount, succeededCount, failedCount, totalCount))
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
104
internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
Normal file
104
internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package wangsucdn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"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/wangsu-certificate"
|
||||||
|
wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 网宿云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 网宿云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 加速域名数组。
|
||||||
|
Domains []string `json:"domains"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *wangsusdk.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
// 上传证书到证书管理
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量修改域名证书配置
|
||||||
|
// REF: https://www.wangsu.com/document/api-doc/37447
|
||||||
|
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||||
|
batchUpdateCertificateConfigReq := &wangsusdk.BatchUpdateCertificateConfigRequest{
|
||||||
|
CertificateId: certId,
|
||||||
|
DomainNames: d.config.Domains,
|
||||||
|
}
|
||||||
|
batchUpdateCertificateConfigResp, err := d.sdkClient.BatchUpdateCertificateConfig(batchUpdateCertificateConfigReq)
|
||||||
|
d.logger.Debug("sdk request 'cdn.BatchUpdateCertificateConfig'", slog.Any("request", batchUpdateCertificateConfigReq), slog.Any("response", batchUpdateCertificateConfigResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'cdn.BatchUpdateCertificateConfig': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) {
|
||||||
|
if accessKeyId == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessKeySecret == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package wangsucdn_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdn"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fDomain string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCDN_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./wangsu_cdn_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCDN_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("DOMAIN: %v", fDomain),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
Domains: []string{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)
|
||||||
|
})
|
||||||
|
}
|
@ -17,7 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||||
wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn"
|
wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdnpro"
|
||||||
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
||||||
)
|
)
|
||||||
@ -88,9 +88,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
|
|
||||||
// 查询已部署加速域名的详情
|
// 查询已部署加速域名的详情
|
||||||
getHostnameDetailResp, err := d.sdkClient.GetHostnameDetail(d.config.Domain)
|
getHostnameDetailResp, err := d.sdkClient.GetHostnameDetail(d.config.Domain)
|
||||||
d.logger.Debug("sdk request 'cdn.GetHostnameDetail'", slog.String("hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp))
|
d.logger.Debug("sdk request 'cdnpro.GetHostnameDetail'", slog.String("hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetHostnameDetail': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetHostnameDetail': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成网宿云证书参数
|
// 生成网宿云证书参数
|
||||||
@ -126,9 +126,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
NewVersion: certificateNewVersionInfo,
|
NewVersion: certificateNewVersionInfo,
|
||||||
}
|
}
|
||||||
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
|
createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq)
|
||||||
d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
|
d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateCertificate': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wangsuCertUrl = createCertificateResp.CertificateUrl
|
wangsuCertUrl = createCertificateResp.CertificateUrl
|
||||||
@ -149,9 +149,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
NewVersion: certificateNewVersionInfo,
|
NewVersion: certificateNewVersionInfo,
|
||||||
}
|
}
|
||||||
updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq)
|
updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq)
|
||||||
d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
|
d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.UpdateCertificate': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.UpdateCertificate': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wangsuCertUrl = updateCertificateResp.CertificateUrl
|
wangsuCertUrl = updateCertificateResp.CertificateUrl
|
||||||
@ -186,9 +186,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
createDeploymentTaskReq.Webhook = typeutil.ToPtr(d.config.WebhookId)
|
createDeploymentTaskReq.Webhook = typeutil.ToPtr(d.config.WebhookId)
|
||||||
}
|
}
|
||||||
createDeploymentTaskResp, err := d.sdkClient.CreateDeploymentTask(createDeploymentTaskReq)
|
createDeploymentTaskResp, err := d.sdkClient.CreateDeploymentTask(createDeploymentTaskReq)
|
||||||
d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp))
|
d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateDeploymentTask': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateDeploymentTask': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 循环获取部署任务详细信息,等待任务状态变更
|
// 循环获取部署任务详细信息,等待任务状态变更
|
||||||
@ -206,9 +206,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
|
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
|
||||||
d.logger.Info("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
|
d.logger.Info("sdk request 'cdnpro.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDeploymentTaskDetail': %w", err)
|
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetDeploymentTaskDetail': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if getDeploymentTaskDetailResp.Status == "failed" {
|
if getDeploymentTaskDetailResp.Status == "failed" {
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
package wangsucertificate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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/wangsu-certificate"
|
||||||
|
wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate"
|
||||||
|
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeployerConfig struct {
|
||||||
|
// 网宿云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 网宿云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
// 证书 ID。
|
||||||
|
// 选填。零值时表示新建证书;否则表示更新证书。
|
||||||
|
CertificateId string `json:"certificateId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeployerProvider struct {
|
||||||
|
config *DeployerConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *wangsusdk.Client
|
||||||
|
sslUploader uploader.Uploader
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ deployer.Deployer = (*DeployerProvider)(nil)
|
||||||
|
|
||||||
|
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||||
|
AccessKeyId: config.AccessKeyId,
|
||||||
|
AccessKeySecret: config.AccessKeySecret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeployerProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
sslUploader: uploader,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
|
||||||
|
if logger == nil {
|
||||||
|
d.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
d.logger = logger
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||||
|
if d.config.CertificateId == "" {
|
||||||
|
// 上传证书到证书管理
|
||||||
|
upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||||
|
} else {
|
||||||
|
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 修改证书
|
||||||
|
// REF: https://www.wangsu.com/document/api-doc/25568?productCode=certificatemanagement
|
||||||
|
updateCertificateReq := &wangsusdk.UpdateCertificateRequest{
|
||||||
|
Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
|
||||||
|
Certificate: typeutil.ToPtr(certPEM),
|
||||||
|
PrivateKey: typeutil.ToPtr(privkeyPEM),
|
||||||
|
Comment: typeutil.ToPtr("upload from certimate"),
|
||||||
|
}
|
||||||
|
updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq)
|
||||||
|
d.logger.Debug("sdk request 'certificatemanagement.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &deployer.DeployResult{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) {
|
||||||
|
if accessKeyId == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessKeySecret == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package wangsucertificate_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-certificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
fCertificateId string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./wangsu_certificate_test.go -args \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_CERTIFICATEID="your-certificate-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("ACCESSKEYID: %v", fAccessKeyId),
|
||||||
|
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||||
|
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
CertificateId: fCertificateId,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
@ -159,9 +159,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
|
|
||||||
// 生成请求
|
// 生成请求
|
||||||
// 其中 GET 请求需转换为查询参数
|
// 其中 GET 请求需转换为查询参数
|
||||||
req := d.httpClient.R().
|
req := d.httpClient.R().SetHeaderMultiValues(webhookHeaders)
|
||||||
SetContext(ctx).
|
|
||||||
SetHeaderMultiValues(webhookHeaders)
|
|
||||||
req.URL = webhookUrl.String()
|
req.URL = webhookUrl.String()
|
||||||
req.Method = webhookMethod
|
req.Method = webhookMethod
|
||||||
if webhookMethod == http.MethodGet {
|
if webhookMethod == http.MethodGet {
|
||||||
@ -178,7 +176,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
resp, err := req.SetDebug(true).Send()
|
resp, err := req.Send()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
|
@ -2,10 +2,10 @@ package bark
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/nikoksr/notify"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/nikoksr/notify/service/bark"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -19,8 +19,9 @@ type NotifierConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -30,9 +31,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,16 +50,25 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
var srv notify.Notifier
|
const defaultServerURL = "https://api.day.app/"
|
||||||
if n.config.ServerUrl == "" {
|
serverUrl := defaultServerURL
|
||||||
srv = bark.New(n.config.DeviceKey)
|
if n.config.ServerUrl != "" {
|
||||||
} else {
|
serverUrl = n.config.ServerUrl
|
||||||
srv = bark.NewWithServers(n.config.DeviceKey, n.config.ServerUrl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
// REF: https://bark.day.app/#/tutorial
|
||||||
|
req := n.httpClient.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(map[string]any{
|
||||||
|
"title": subject,
|
||||||
|
"body": message,
|
||||||
|
"device_key": n.config.DeviceKey,
|
||||||
|
})
|
||||||
|
resp, err := req.Post(serverUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("bark api error: failed to send request: %w", err)
|
||||||
|
} else if resp.IsError() {
|
||||||
|
return nil, fmt.Errorf("bark api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package dingtalk
|
package dingtalkbot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/dingding"
|
"github.com/blinkbean/dingtalk"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -48,17 +48,18 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid webhook url: %w", err)
|
return nil, fmt.Errorf("dingtalk api error: invalid webhook url: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := dingding.New(&dingding.Config{
|
var bot *dingtalk.DingTalk
|
||||||
Token: webhookUrl.Query().Get("access_token"),
|
if n.config.Secret == "" {
|
||||||
Secret: n.config.Secret,
|
bot = dingtalk.InitDingTalk([]string{webhookUrl.Query().Get("access_token")}, "")
|
||||||
})
|
} else {
|
||||||
|
bot = dingtalk.InitDingTalkWithSecret(webhookUrl.Query().Get("access_token"), n.config.Secret)
|
||||||
|
}
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
if err := bot.SendTextMessage(subject + "\n" + message); err != nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("dingtalk api error: %w", err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
@ -1,4 +1,4 @@
|
|||||||
package dingtalk_test
|
package dingtalkbot_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalk"
|
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -16,22 +16,23 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fAccessToken string
|
fWebhookUrl string
|
||||||
fSecret string
|
fSecret string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_NOTIFIER_DINGTALK_"
|
argsPrefix := "CERTIMATE_NOTIFIER_DINGTALKBOT_"
|
||||||
|
|
||||||
flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "")
|
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||||
flag.StringVar(&fSecret, argsPrefix+"SECRET", "", "")
|
flag.StringVar(&fSecret, argsPrefix+"SECRET", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./dingtalk_test.go -args \
|
go test -v ./dingtalkbot_test.go -args \
|
||||||
--CERTIMATE_NOTIFIER_DINGTALK_URL="https://example.com/your-webhook-url"
|
--CERTIMATE_NOTIFIER_DINGTALKBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
|
||||||
|
--CERTIMATE_NOTIFIER_DINGTALKBOT_SECRET="your-secret"
|
||||||
*/
|
*/
|
||||||
func TestNotify(t *testing.T) {
|
func TestNotify(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -39,13 +40,13 @@ func TestNotify(t *testing.T) {
|
|||||||
t.Run("Notify", func(t *testing.T) {
|
t.Run("Notify", func(t *testing.T) {
|
||||||
t.Log(strings.Join([]string{
|
t.Log(strings.Join([]string{
|
||||||
"args:",
|
"args:",
|
||||||
fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken),
|
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
||||||
fmt.Sprintf("SECRET: %v", fSecret),
|
fmt.Sprintf("SECRET: %v", fSecret),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
AccessToken: fAccessToken,
|
WebhookUrl: fWebhookUrl,
|
||||||
Secret: fSecret,
|
Secret: fSecret,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
@ -17,7 +17,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
fSmtpHost string
|
fSmtpHost string
|
||||||
fSmtpPort int
|
fSmtpPort int64
|
||||||
fSmtpTLS bool
|
fSmtpTLS bool
|
||||||
fUsername string
|
fUsername string
|
||||||
fPassword string
|
fPassword string
|
||||||
@ -29,7 +29,7 @@ func init() {
|
|||||||
argsPrefix := "CERTIMATE_NOTIFIER_EMAIL_"
|
argsPrefix := "CERTIMATE_NOTIFIER_EMAIL_"
|
||||||
|
|
||||||
flag.StringVar(&fSmtpHost, argsPrefix+"SMTPHOST", "", "")
|
flag.StringVar(&fSmtpHost, argsPrefix+"SMTPHOST", "", "")
|
||||||
flag.IntVar(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "")
|
flag.Int64Var(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "")
|
||||||
flag.BoolVar(&fSmtpTLS, argsPrefix+"SMTPTLS", false, "")
|
flag.BoolVar(&fSmtpTLS, argsPrefix+"SMTPTLS", false, "")
|
||||||
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
|
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
|
||||||
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
|
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
package gotify
|
package gotify
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// Gotify 服务地址。
|
// Gotify 服务地址。
|
||||||
Url string `json:"url"`
|
ServerUrl string `json:"serverUrl"`
|
||||||
// Gotify Token。
|
// Gotify Token。
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
// Gotify 消息优先级。
|
// Gotify 消息优先级。
|
||||||
@ -24,7 +23,7 @@ type NotifierConfig struct {
|
|||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
httpClient *http.Client
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -34,10 +33,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,45 +52,22 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
reqBody := &struct {
|
serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
|
||||||
Title string `json:"title"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Priority int64 `json:"priority"`
|
|
||||||
}{
|
|
||||||
Title: subject,
|
|
||||||
Message: message,
|
|
||||||
Priority: n.config.Priority,
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(reqBody)
|
// REF: https://gotify.net/api-docs#/message/createMessage
|
||||||
if err != nil {
|
req := n.httpClient.R().
|
||||||
return nil, fmt.Errorf("gotify api error: failed to encode message body: %w", err)
|
SetHeader("Content-Type", "application/json").
|
||||||
}
|
SetHeader("Authorization", "Bearer "+n.config.Token).
|
||||||
|
SetBody(map[string]any{
|
||||||
req, err := http.NewRequestWithContext(
|
"title": subject,
|
||||||
ctx,
|
"message": message,
|
||||||
http.MethodPost,
|
"priority": n.config.Priority,
|
||||||
fmt.Sprintf("%s/message", n.config.Url),
|
})
|
||||||
bytes.NewReader(body),
|
resp, err := req.Post(fmt.Sprintf("%s/message", serverUrl))
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("gotify api error: failed to create new request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", n.config.Token))
|
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
|
|
||||||
resp, err := n.httpClient.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("gotify api error: failed to send request: %w", err)
|
return nil, fmt.Errorf("gotify api error: failed to send request: %w", err)
|
||||||
}
|
} else if resp.IsError() {
|
||||||
defer resp.Body.Close()
|
return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("gotify api error: failed to read response: %w", err)
|
|
||||||
} else if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -48,9 +48,9 @@ func TestNotify(t *testing.T) {
|
|||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
Url: fUrl,
|
ServerUrl: fUrl,
|
||||||
Token: fToken,
|
Token: fToken,
|
||||||
Priority: fPriority,
|
Priority: fPriority,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package lark
|
package larkbot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/lark"
|
"github.com/go-lark/lark"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -42,11 +43,17 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := lark.NewWebhookService(n.config.WebhookUrl)
|
bot := lark.NewNotificationBot(n.config.WebhookUrl)
|
||||||
|
content := lark.NewPostBuilder().
|
||||||
err = srv.Send(ctx, subject, message)
|
Title(subject).
|
||||||
|
TextTag(message, 1, false).
|
||||||
|
Render()
|
||||||
|
msg := lark.NewMsgBuffer(lark.MsgPost).Post(content)
|
||||||
|
resp, err := bot.PostNotificationV2(msg.Build())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("lark api error: %w", err)
|
||||||
|
} else if resp.Code != 0 {
|
||||||
|
return nil, fmt.Errorf("lark api error: code='%d', message='%s'", resp.Code, resp.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
@ -1,4 +1,4 @@
|
|||||||
package serverchan_test
|
package larkbot_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecom"
|
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -18,7 +18,7 @@ const (
|
|||||||
var fWebhookUrl string
|
var fWebhookUrl string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_NOTIFIER_WECOM_"
|
argsPrefix := "CERTIMATE_NOTIFIER_LARKBOT_"
|
||||||
|
|
||||||
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||||
}
|
}
|
||||||
@ -26,8 +26,8 @@ func init() {
|
|||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./wecom_test.go -args \
|
go test -v ./larkbot_test.go -args \
|
||||||
--CERTIMATE_NOTIFIER_WECOM_WEBHOOKURL="https://example.com/your-webhook-url" \
|
--CERTIMATE_NOTIFIER_LARKBOT_WEBHOOKURL="https://example.com/your-webhook-url"
|
||||||
*/
|
*/
|
||||||
func TestNotify(t *testing.T) {
|
func TestNotify(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
@ -1,15 +1,13 @@
|
|||||||
package mattermost
|
package mattermost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/mattermost"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,8 +23,9 @@ type NotifierConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -36,9 +35,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,17 +54,29 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := mattermost.New(strings.TrimRight(n.config.ServerUrl, "/"))
|
serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
|
||||||
|
|
||||||
if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil {
|
// REF: https://developers.mattermost.com/api-documentation/#/operations/Login
|
||||||
return nil, err
|
loginReq := n.httpClient.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(map[string]any{
|
||||||
|
"login_id": n.config.Username,
|
||||||
|
"password": n.config.Password,
|
||||||
|
})
|
||||||
|
loginResp, err := loginReq.Post(fmt.Sprintf("%s/api/v4/users/login", serverUrl))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err)
|
||||||
|
} else if loginResp.IsError() {
|
||||||
|
return nil, fmt.Errorf("mattermost api error: unexpected status code: %d, resp: %s", loginResp.StatusCode(), loginResp.String())
|
||||||
|
} else if loginResp.Header().Get("Token") == "" {
|
||||||
|
return nil, fmt.Errorf("mattermost api error: received empty login token")
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.AddReceivers(n.config.ChannelId)
|
// REF: https://developers.mattermost.com/api-documentation/#/operations/CreatePost
|
||||||
|
postReq := n.httpClient.R().
|
||||||
// 复写消息样式
|
SetHeader("Content-Type", "application/json").
|
||||||
srv.PreSend(func(req *http.Request) error {
|
SetHeader("Authorization", "Bearer "+loginResp.Header().Get("Token")).
|
||||||
m := map[string]interface{}{
|
SetBody(map[string]any{
|
||||||
"channel_id": n.config.ChannelId,
|
"channel_id": n.config.ChannelId,
|
||||||
"props": map[string]interface{}{
|
"props": map[string]interface{}{
|
||||||
"attachments": []map[string]interface{}{
|
"attachments": []map[string]interface{}{
|
||||||
@ -72,20 +86,12 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
postResp, err := postReq.Post(fmt.Sprintf("%s/api/v4/posts", serverUrl))
|
||||||
if body, err := json.Marshal(m); err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err)
|
||||||
} else {
|
} else if postResp.IsError() {
|
||||||
req.ContentLength = int64(len(body))
|
return nil, fmt.Errorf("mattermost api error: unexpected status code: %d, resp: %s", postResp.StatusCode(), postResp.String())
|
||||||
req.Body = io.NopCloser(bytes.NewReader(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err = srv.Send(ctx, subject, message); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package pushover
|
package pushover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -22,7 +20,7 @@ type NotifierConfig struct {
|
|||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
httpClient *http.Client
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -32,10 +30,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,46 +50,19 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
// REF: https://pushover.net/api
|
// REF: https://pushover.net/api
|
||||||
reqBody := &struct {
|
req := n.httpClient.R().
|
||||||
Token string `json:"token"`
|
SetHeader("Content-Type", "application/json").
|
||||||
User string `json:"user"`
|
SetBody(map[string]any{
|
||||||
Title string `json:"title"`
|
"title": subject,
|
||||||
Message string `json:"message"`
|
"message": message,
|
||||||
}{
|
"token": n.config.Token,
|
||||||
Token: n.config.Token,
|
"user": n.config.User,
|
||||||
User: n.config.User,
|
})
|
||||||
Title: subject,
|
resp, err := req.Post("https://api.pushover.net/1/messages.json")
|
||||||
Message: message,
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(reqBody)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushover api error: failed to encode message body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
|
||||||
ctx,
|
|
||||||
http.MethodPost,
|
|
||||||
"https://api.pushover.net/1/messages.json",
|
|
||||||
bytes.NewReader(body),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushover api error: failed to create new request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
|
|
||||||
resp, err := n.httpClient.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("pushover api error: failed to send request: %w", err)
|
return nil, fmt.Errorf("pushover api error: failed to send request: %w", err)
|
||||||
}
|
} else if resp.IsError() {
|
||||||
defer resp.Body.Close()
|
return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushover api error: failed to read response: %w", err)
|
|
||||||
} else if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package pushplus
|
package pushplus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -20,7 +19,7 @@ type NotifierConfig struct {
|
|||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
httpClient *http.Client
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -30,10 +29,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
httpClient: http.DefaultClient,
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,55 +48,29 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
// REF: https://pushplus.plus/doc/guide/api.html
|
// REF: https://pushplus.plus/doc/guide/api.html#%E4%B8%80%E3%80%81%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3
|
||||||
reqBody := &struct {
|
req := n.httpClient.R().
|
||||||
Token string `json:"token"`
|
SetHeader("Content-Type", "application/json").
|
||||||
Title string `json:"title"`
|
SetBody(map[string]any{
|
||||||
Content string `json:"content"`
|
"title": subject,
|
||||||
}{
|
"content": message,
|
||||||
Token: n.config.Token,
|
"token": n.config.Token,
|
||||||
Title: subject,
|
})
|
||||||
Content: message,
|
resp, err := req.Post("https://www.pushplus.plus/send")
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(reqBody)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushplus api error: failed to encode message body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(
|
|
||||||
ctx,
|
|
||||||
http.MethodPost,
|
|
||||||
"https://www.pushplus.plus/send",
|
|
||||||
bytes.NewReader(body),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushplus api error: failed to create new request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
|
|
||||||
resp, err := n.httpClient.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err)
|
return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err)
|
||||||
}
|
} else if resp.IsError() {
|
||||||
defer resp.Body.Close()
|
return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
|
|
||||||
result, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("pushplus api error: failed to read response: %w", err)
|
|
||||||
} else if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode, string(result))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorResponse struct {
|
var errorResponse struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Msg string `json:"msg"`
|
Message string `json:"msg"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(result, &errorResponse); err != nil {
|
if err := json.Unmarshal(resp.Body(), &errorResponse); err != nil {
|
||||||
return nil, fmt.Errorf("pushplus api error: failed to decode response: %w", err)
|
return nil, fmt.Errorf("pushplus api error: failed to unmarshal response: %w", err)
|
||||||
} else if errorResponse.Code != 200 {
|
} else if errorResponse.Code != 200 {
|
||||||
return nil, fmt.Errorf("pushplus api error: unexpected response code: %d, msg: %s", errorResponse.Code, errorResponse.Msg)
|
return nil, fmt.Errorf("pushplus api error: code='%d', message='%s'", errorResponse.Code, errorResponse.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -2,22 +2,23 @@ package serverchan
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
notifyHttp "github.com/nikoksr/notify/service/http"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotifierConfig struct {
|
type NotifierConfig struct {
|
||||||
// ServerChan 服务地址。
|
// ServerChan 服务地址。
|
||||||
Url string `json:"url"`
|
ServerUrl string `json:"serverUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -27,9 +28,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,24 +47,18 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := notifyHttp.New()
|
// REF: https://sct.ftqq.com/
|
||||||
|
req := n.httpClient.R().
|
||||||
srv.AddReceivers(¬ifyHttp.Webhook{
|
SetHeader("Content-Type", "application/json").
|
||||||
URL: n.config.Url,
|
SetBody(map[string]any{
|
||||||
Header: http.Header{},
|
"text": subject,
|
||||||
ContentType: "application/json",
|
"desp": message,
|
||||||
Method: http.MethodPost,
|
})
|
||||||
BuildPayload: func(subject, message string) (payload any) {
|
resp, err := req.Post(n.config.ServerUrl)
|
||||||
return map[string]string{
|
|
||||||
"text": subject,
|
|
||||||
"desp": message,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("serverchan api error: failed to send request: %w", err)
|
||||||
|
} else if resp.IsError() {
|
||||||
|
return nil, fmt.Errorf("serverchan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
|
@ -39,7 +39,7 @@ func TestNotify(t *testing.T) {
|
|||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
|
||||||
Url: fUrl,
|
ServerUrl: fUrl,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package telegram
|
package telegrambot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/nikoksr/notify/service/telegram"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -17,8 +18,9 @@ type NotifierConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -28,9 +30,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
logger: slog.Default(),
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,16 +49,18 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv, err := telegram.New(n.config.BotToken)
|
// REF: https://core.telegram.org/bots/api#sendmessage
|
||||||
|
req := n.httpClient.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(map[string]any{
|
||||||
|
"chat_id": n.config.ChatId,
|
||||||
|
"text": subject + "\n" + message,
|
||||||
|
})
|
||||||
|
resp, err := req.Post(fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.config.BotToken))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("telegram api error: failed to send request: %w", err)
|
||||||
}
|
} else if resp.IsError() {
|
||||||
|
return nil, fmt.Errorf("telegram api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
srv.AddReceivers(n.config.ChatId)
|
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
@ -1,4 +1,4 @@
|
|||||||
package telegram_test
|
package telegrambot_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
|
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -21,7 +21,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAM_"
|
argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAMBOT_"
|
||||||
|
|
||||||
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||||
flag.Int64Var(&fChartId, argsPrefix+"CHATID", 0, "")
|
flag.Int64Var(&fChartId, argsPrefix+"CHATID", 0, "")
|
||||||
@ -30,9 +30,9 @@ func init() {
|
|||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./telegram_test.go -args \
|
go test -v ./telegrambot_test.go -args \
|
||||||
--CERTIMATE_NOTIFIER_TELEGRAM_APITOKEN="your-api-token" \
|
--CERTIMATE_NOTIFIER_TELEGRAMBOT_APITOKEN="your-api-token" \
|
||||||
--CERTIMATE_NOTIFIER_TELEGRAM_CHATID=123456
|
--CERTIMATE_NOTIFIER_TELEGRAMBOT_CHATID=123456
|
||||||
*/
|
*/
|
||||||
func TestNotify(t *testing.T) {
|
func TestNotify(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
@ -139,9 +139,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
|
|
||||||
// 生成请求
|
// 生成请求
|
||||||
// 其中 GET 请求需转换为查询参数
|
// 其中 GET 请求需转换为查询参数
|
||||||
req := n.httpClient.R().
|
req := n.httpClient.R().SetHeaderMultiValues(webhookHeaders)
|
||||||
SetContext(ctx).
|
|
||||||
SetHeaderMultiValues(webhookHeaders)
|
|
||||||
req.URL = webhookUrl.String()
|
req.URL = webhookUrl.String()
|
||||||
req.Method = webhookMethod
|
req.Method = webhookMethod
|
||||||
if webhookMethod == http.MethodGet {
|
if webhookMethod == http.MethodGet {
|
||||||
@ -160,12 +158,12 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
resp, err := req.Send()
|
resp, err := req.Send()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to send webhook request: %w", err)
|
return nil, fmt.Errorf("webhook error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode())
|
return nil, fmt.Errorf("webhook error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
n.logger.Debug("webhook responded", slog.Any("response", resp.String()))
|
n.logger.Debug("webhook responded", slog.String("response", resp.String()))
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package serverchan
|
package wecombot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
notifyHttp "github.com/nikoksr/notify/service/http"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
"github.com/usual2970/certimate/internal/pkg/core/notifier"
|
||||||
)
|
)
|
||||||
@ -16,8 +16,9 @@ type NotifierConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotifierProvider struct {
|
type NotifierProvider struct {
|
||||||
config *NotifierConfig
|
config *NotifierConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
httpClient *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
var _ notifier.Notifier = (*NotifierProvider)(nil)
|
||||||
@ -27,8 +28,12 @@ func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
return &NotifierProvider{
|
return &NotifierProvider{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
httpClient: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,26 +47,20 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
|
||||||
srv := notifyHttp.New()
|
// REF: https://developer.work.weixin.qq.com/document/path/91770
|
||||||
|
req := n.httpClient.R().
|
||||||
srv.AddReceivers(¬ifyHttp.Webhook{
|
SetHeader("Content-Type", "application/json").
|
||||||
URL: n.config.WebhookUrl,
|
SetBody(map[string]any{
|
||||||
Header: http.Header{},
|
"msgtype": "text",
|
||||||
ContentType: "application/json",
|
"text": map[string]string{
|
||||||
Method: http.MethodPost,
|
"content": subject + "\n\n" + message,
|
||||||
BuildPayload: func(subject, message string) (payload any) {
|
},
|
||||||
return map[string]any{
|
})
|
||||||
"msgtype": "text",
|
resp, err := req.Post(n.config.WebhookUrl)
|
||||||
"text": map[string]string{
|
|
||||||
"content": subject + "\n\n" + message,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
err = srv.Send(ctx, subject, message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("wecom api error: failed to send request: %w", err)
|
||||||
|
} else if resp.IsError() {
|
||||||
|
return nil, fmt.Errorf("wecom api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return ¬ifier.NotifyResult{}, nil
|
return ¬ifier.NotifyResult{}, nil
|
@ -1,4 +1,4 @@
|
|||||||
package lark_test
|
package wecombot_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
|
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -18,7 +18,7 @@ const (
|
|||||||
var fWebhookUrl string
|
var fWebhookUrl string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
argsPrefix := "CERTIMATE_NOTIFIER_LARK_"
|
argsPrefix := "CERTIMATE_NOTIFIER_WECOMBOT_"
|
||||||
|
|
||||||
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||||
}
|
}
|
||||||
@ -26,8 +26,8 @@ func init() {
|
|||||||
/*
|
/*
|
||||||
Shell command to run this test:
|
Shell command to run this test:
|
||||||
|
|
||||||
go test -v ./lark_test.go -args \
|
go test -v ./wecombot_test.go -args \
|
||||||
--CERTIMATE_NOTIFIER_LARK_WEBHOOKURL="https://example.com/your-webhook-url"
|
--CERTIMATE_NOTIFIER_WECOMBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
|
||||||
*/
|
*/
|
||||||
func TestNotify(t *testing.T) {
|
func TestNotify(t *testing.T) {
|
||||||
flag.Parse()
|
flag.Parse()
|
@ -10,12 +10,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UploaderConfig struct {
|
type UploaderConfig struct {
|
||||||
// 1Panel 地址。
|
// 1Panel 地址。
|
||||||
ApiUrl string `json:"apiUrl"`
|
ApiUrl string `json:"apiUrl"`
|
||||||
|
// 1Panel 版本。
|
||||||
|
ApiVersion string `json:"apiVersion"`
|
||||||
// 1Panel 接口密钥。
|
// 1Panel 接口密钥。
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
}
|
}
|
||||||
@ -23,7 +25,7 @@ type UploaderConfig struct {
|
|||||||
type UploaderProvider struct {
|
type UploaderProvider struct {
|
||||||
config *UploaderConfig
|
config *UploaderConfig
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sdkClient *opsdk.Client
|
sdkClient *onepanelsdk.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
||||||
@ -33,7 +35,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
|
|||||||
panic("config is nil")
|
panic("config is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey)
|
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
}
|
}
|
||||||
@ -67,7 +69,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
|
|||||||
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
// 上传证书
|
// 上传证书
|
||||||
uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
|
uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
|
||||||
Type: "paste",
|
Type: "paste",
|
||||||
Description: certName,
|
Description: certName,
|
||||||
Certificate: certPEM,
|
Certificate: certPEM,
|
||||||
@ -99,7 +101,7 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{
|
searchWebsiteSSLReq := &onepanelsdk.SearchWebsiteSSLRequest{
|
||||||
Page: searchWebsiteSSLPageNumber,
|
Page: searchWebsiteSSLPageNumber,
|
||||||
PageSize: searchWebsiteSSLPageSize,
|
PageSize: searchWebsiteSSLPageSize,
|
||||||
}
|
}
|
||||||
@ -130,15 +132,19 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) {
|
func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) {
|
||||||
if _, err := url.Parse(apiUrl); err != nil {
|
if _, err := url.Parse(apiUrl); err != nil {
|
||||||
return nil, errors.New("invalid 1panel api url")
|
return nil, errors.New("invalid 1panel api url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apiVersion == "" {
|
||||||
|
return nil, errors.New("invalid 1panel api version")
|
||||||
|
}
|
||||||
|
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
return nil, errors.New("invalid 1panel api key")
|
return nil, errors.New("invalid 1panel api key")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := opsdk.NewClient(apiUrl, apiKey)
|
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ var (
|
|||||||
fInputCertPath string
|
fInputCertPath string
|
||||||
fInputKeyPath string
|
fInputKeyPath string
|
||||||
fApiUrl string
|
fApiUrl string
|
||||||
|
fApiVersion string
|
||||||
fApiKey string
|
fApiKey string
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ func init() {
|
|||||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
|
||||||
|
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
|
||||||
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +37,7 @@ Shell command to run this test:
|
|||||||
--CERTIMATE_UPLOADER_1PANELSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
--CERTIMATE_UPLOADER_1PANELSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
--CERTIMATE_UPLOADER_1PANELSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
--CERTIMATE_UPLOADER_1PANELSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
--CERTIMATE_UPLOADER_1PANELSSL_APIURL="http://127.0.0.1:20410" \
|
--CERTIMATE_UPLOADER_1PANELSSL_APIURL="http://127.0.0.1:20410" \
|
||||||
|
--CERTIMATE_UPLOADER_1PANELSSL_APIVERSION="v1" \
|
||||||
--CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key"
|
--CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key"
|
||||||
*/
|
*/
|
||||||
func TestDeploy(t *testing.T) {
|
func TestDeploy(t *testing.T) {
|
||||||
@ -46,12 +49,14 @@ func TestDeploy(t *testing.T) {
|
|||||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||||
fmt.Sprintf("APIURL: %v", fApiUrl),
|
fmt.Sprintf("APIURL: %v", fApiUrl),
|
||||||
|
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||||
}, "\n"))
|
}, "\n"))
|
||||||
|
|
||||||
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
||||||
ApiUrl: fApiUrl,
|
ApiUrl: fApiUrl,
|
||||||
ApiKey: fApiKey,
|
ApiVersion: fApiVersion,
|
||||||
|
ApiKey: fApiKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("err: %+v", err)
|
t.Errorf("err: %+v", err)
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package jdcloudssl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate"
|
||||||
|
|
||||||
|
"github.com/usual2970/certimate/internal/pkg/core/uploader"
|
||||||
|
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||||
|
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploaderConfig struct {
|
||||||
|
// 网宿云 AccessKeyId。
|
||||||
|
AccessKeyId string `json:"accessKeyId"`
|
||||||
|
// 网宿云 AccessKeySecret。
|
||||||
|
AccessKeySecret string `json:"accessKeySecret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploaderProvider struct {
|
||||||
|
config *UploaderConfig
|
||||||
|
logger *slog.Logger
|
||||||
|
sdkClient *wangsusdk.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ uploader.Uploader = (*UploaderProvider)(nil)
|
||||||
|
|
||||||
|
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
panic("config is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UploaderProvider{
|
||||||
|
config: config,
|
||||||
|
logger: slog.Default(),
|
||||||
|
sdkClient: client,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader {
|
||||||
|
if logger == nil {
|
||||||
|
u.logger = slog.Default()
|
||||||
|
} else {
|
||||||
|
u.logger = logger
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) {
|
||||||
|
// 解析证书内容
|
||||||
|
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询证书列表,避免重复上传
|
||||||
|
// REF: https://www.wangsu.com/document/api-doc/26426
|
||||||
|
listCertificatesResp, err := u.sdkClient.ListCertificates()
|
||||||
|
u.logger.Debug("sdk request 'certificatemanagement.ListCertificates'", slog.Any("response", listCertificatesResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.ListCertificates': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listCertificatesResp.Certificates != nil {
|
||||||
|
for _, certificate := range listCertificatesResp.Certificates {
|
||||||
|
// 对比证书序列号
|
||||||
|
if !strings.EqualFold(certX509.SerialNumber.Text(16), certificate.Serial) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再对比证书有效期
|
||||||
|
cstzone := time.FixedZone("CST", 8*60*60)
|
||||||
|
oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certificate.ValidityFrom, cstzone)
|
||||||
|
oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certificate.ValidityTo, cstzone)
|
||||||
|
if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||||
|
u.logger.Info("ssl certificate already exists")
|
||||||
|
return &uploader.UploadResult{
|
||||||
|
CertId: certificate.CertificateId,
|
||||||
|
CertName: certificate.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成新证书名(需符合网宿云命名规则)
|
||||||
|
var certId string
|
||||||
|
certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
|
||||||
|
|
||||||
|
// 新增证书
|
||||||
|
// REF: https://www.wangsu.com/document/api-doc/25199?productCode=certificatemanagement
|
||||||
|
createCertificateReq := &wangsusdk.CreateCertificateRequest{
|
||||||
|
Name: typeutil.ToPtr(certName),
|
||||||
|
Certificate: typeutil.ToPtr(certPEM),
|
||||||
|
PrivateKey: typeutil.ToPtr(privkeyPEM),
|
||||||
|
Comment: typeutil.ToPtr("upload from certimate"),
|
||||||
|
}
|
||||||
|
createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq)
|
||||||
|
u.logger.Debug("sdk request 'certificatemanagement.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网宿云证书 URL 中包含证书 ID
|
||||||
|
// 格式:
|
||||||
|
// https://open.chinanetcenter.com/api/certificate/100001
|
||||||
|
wangsuCertIdMatches := regexp.MustCompile(`/certificate/([0-9]+)`).FindStringSubmatch(createCertificateResp.CertificateUrl)
|
||||||
|
if len(wangsuCertIdMatches) > 1 {
|
||||||
|
certId = wangsuCertIdMatches[1]
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("received empty certificate id")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &uploader.UploadResult{
|
||||||
|
CertId: certId,
|
||||||
|
CertName: certName,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) {
|
||||||
|
if accessKeyId == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if accessKeySecret == "" {
|
||||||
|
return nil, errors.New("invalid wangsu access key secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package jdcloudssl_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/wangsu-certificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fInputCertPath string
|
||||||
|
fInputKeyPath string
|
||||||
|
fAccessKeyId string
|
||||||
|
fAccessKeySecret string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
argsPrefix := "CERTIMATE_UPLOADER_JDCLOUDSSL_"
|
||||||
|
|
||||||
|
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||||
|
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||||
|
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||||
|
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shell command to run this test:
|
||||||
|
|
||||||
|
go test -v ./wangsu_certificate_test.go -args \
|
||||||
|
--CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||||
|
--CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
|
||||||
|
--CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret"
|
||||||
|
*/
|
||||||
|
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),
|
||||||
|
}, "\n"))
|
||||||
|
|
||||||
|
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
||||||
|
AccessKeyId: fAccessKeyId,
|
||||||
|
AccessKeySecret: fAccessKeySecret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||||
|
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||||
|
res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sres, _ := json.Marshal(res)
|
||||||
|
t.Logf("ok: %s", string(sres))
|
||||||
|
})
|
||||||
|
}
|
@ -14,19 +14,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiHost string
|
apiKey string
|
||||||
apiKey string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiHost, apiKey string) *Client {
|
func NewClient(apiHost, apiVersion, apiKey string) *Client {
|
||||||
client := resty.New()
|
if apiVersion == "" {
|
||||||
|
apiVersion = "v1"
|
||||||
|
}
|
||||||
|
|
||||||
|
client := resty.New().
|
||||||
|
SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion).
|
||||||
|
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
tokenMd5 := md5.Sum([]byte("1panel" + apiKey + timestamp))
|
||||||
|
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||||
|
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||||
|
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiHost: strings.TrimRight(apiHost, "/"),
|
client: client,
|
||||||
apiKey: apiKey,
|
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,16 +51,8 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) generateToken(timestamp string) string {
|
|
||||||
tokenMd5 := md5.Sum([]byte("1panel" + c.apiKey + timestamp))
|
|
||||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
|
||||||
return tokenMd5Hex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R()
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = c.apiHost + "/api/v1" + path
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := make(map[string]string)
|
qs := make(map[string]string)
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -65,21 +68,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
|
|
||||||
req = req.SetQueryParams(qs)
|
req = req.SetQueryParams(qs)
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
resp, err := req.Execute(method, path)
|
||||||
token := c.generateToken(timestamp)
|
|
||||||
req.SetHeader("1Panel-Timestamp", timestamp)
|
|
||||||
req.SetHeader("1Panel-Token", token)
|
|
||||||
|
|
||||||
resp, err := req.Send()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("1panel api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("1panel api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("1panel api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("1panel api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -95,7 +91,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("1panel api error: failed to parse response: %w", err)
|
return fmt.Errorf("1panel api error: failed to unmarshal response: %w", err)
|
||||||
} else if errcode := result.GetCode(); errcode/100 != 2 {
|
} else if errcode := result.GetCode(); errcode/100 != 2 {
|
||||||
return fmt.Errorf("1panel api error: code='%d', message='%s'", errcode, result.GetMessage())
|
return fmt.Errorf("1panel api error: code='%d', message='%s'", errcode, result.GetMessage())
|
||||||
}
|
}
|
||||||
|
@ -13,17 +13,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiToken string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiToken string) *Client {
|
func NewClient(apiToken string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL("https://cdn.api.baishan.com").
|
||||||
|
SetHeader("token", apiToken)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiToken: apiToken,
|
client: client,
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,8 +33,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
|||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R()
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = "https://cdn.api.baishan.com" + path
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := url.Values{}
|
qs := url.Values{}
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -61,21 +58,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req = req.
|
req = req.SetQueryParamsFromValues(qs)
|
||||||
SetQueryParam("token", c.apiToken).
|
|
||||||
SetQueryParamsFromValues(qs)
|
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetQueryParam("token", c.apiToken).
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := req.Send()
|
resp, err := req.Execute(method, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("baishan api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("baishan api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("baishan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("baishan api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -91,7 +83,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("baishan api error: failed to parse response: %w", err)
|
return fmt.Errorf("baishan api error: failed to unmarshal response: %w", err)
|
||||||
} else if errcode := result.GetCode(); errcode != 0 {
|
} else if errcode := result.GetCode(); errcode != 0 {
|
||||||
return fmt.Errorf("baishan api error: code='%d', message='%s'", errcode, result.GetMessage())
|
return fmt.Errorf("baishan api error: code='%d', message='%s'", errcode, result.GetMessage())
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiHost string
|
apiKey string
|
||||||
apiKey string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiHost, apiKey string) *Client {
|
func NewClient(apiHost, apiKey string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL(strings.TrimRight(apiHost, "/"))
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiHost: strings.TrimRight(apiHost, "/"),
|
apiKey: apiKey,
|
||||||
apiKey: apiKey,
|
client: client,
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,15 +77,14 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response,
|
|||||||
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
||||||
data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp))
|
data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp))
|
||||||
|
|
||||||
url := c.apiHost + path
|
|
||||||
req := c.client.R().
|
req := c.client.R().
|
||||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||||
SetFormData(data)
|
SetFormData(data)
|
||||||
resp, err := req.Post(url)
|
resp, err := req.Post(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("baota api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("baota api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -99,7 +97,7 @@ func (c *Client) sendRequestWithResult(path string, params interface{}, result B
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("baota api error: failed to parse response: %w", err)
|
return fmt.Errorf("baota api error: failed to unmarshal response: %w", err)
|
||||||
} else if errstatus := result.GetStatus(); errstatus != nil && !*errstatus {
|
} else if errstatus := result.GetStatus(); errstatus != nil && !*errstatus {
|
||||||
if result.GetMessage() == nil {
|
if result.GetMessage() == nil {
|
||||||
return fmt.Errorf("baota api error: unknown error")
|
return fmt.Errorf("baota api error: unknown error")
|
||||||
|
19
internal/pkg/sdk3rd/btwaf/api.go
Normal file
19
internal/pkg/sdk3rd/btwaf/api.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package btwaf
|
||||||
|
|
||||||
|
func (c *Client) GetSiteList(req *GetSiteListRequest) (*GetSiteListResponse, error) {
|
||||||
|
resp := &GetSiteListResponse{}
|
||||||
|
err := c.sendRequestWithResult("/wafmastersite/get_site_list", req, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ModifySite(req *ModifySiteRequest) (*ModifySiteResponse, error) {
|
||||||
|
resp := &ModifySiteResponse{}
|
||||||
|
err := c.sendRequestWithResult("/wafmastersite/modify_site", req, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ConfigSetSSL(req *ConfigSetSSLRequest) (*ConfigSetSSLResponse, error) {
|
||||||
|
resp := &ConfigSetSSLResponse{}
|
||||||
|
err := c.sendRequestWithResult("/config/set_cert", req, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
77
internal/pkg/sdk3rd/btwaf/client.go
Normal file
77
internal/pkg/sdk3rd/btwaf/client.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package btwaf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *resty.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(apiHost, apiKey string) *Client {
|
||||||
|
client := resty.New().
|
||||||
|
SetBaseURL(strings.TrimRight(apiHost, "/") + "/api").
|
||||||
|
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||||
|
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
keyMd5 := md5.Sum([]byte(apiKey))
|
||||||
|
keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:]))
|
||||||
|
signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex))
|
||||||
|
signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:]))
|
||||||
|
req.Header.Set("waf_request_time", timestamp)
|
||||||
|
req.Header.Set("waf_request_token", signMd5Hex)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||||
|
c.client.SetTimeout(timeout)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
||||||
|
c.client.SetTLSClientConfig(config)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) {
|
||||||
|
req := c.client.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(params)
|
||||||
|
resp, err := req.Post(path)
|
||||||
|
if err != nil {
|
||||||
|
return resp, fmt.Errorf("baota api error: failed to send request: %w", err)
|
||||||
|
} else if resp.IsError() {
|
||||||
|
return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error {
|
||||||
|
resp, err := c.sendRequest(path, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
|
return fmt.Errorf("baota api error: failed to unmarshal response: %w", err)
|
||||||
|
} else if errcode := result.GetCode(); errcode != 0 {
|
||||||
|
return fmt.Errorf("baota api error: code='%d'", errcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
67
internal/pkg/sdk3rd/btwaf/models.go
Normal file
67
internal/pkg/sdk3rd/btwaf/models.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package btwaf
|
||||||
|
|
||||||
|
type BaseResponse interface {
|
||||||
|
GetCode() int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type baseResponse struct {
|
||||||
|
Code *int32 `json:"code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *baseResponse) GetCode() int32 {
|
||||||
|
if r.Code != nil {
|
||||||
|
return *r.Code
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSiteListRequest struct {
|
||||||
|
Page *int32 `json:"p,omitempty"`
|
||||||
|
PageSize *int32 `json:"p_size,omitempty"`
|
||||||
|
SiteName *string `json:"site_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSiteListResponse struct {
|
||||||
|
baseResponse
|
||||||
|
Result *struct {
|
||||||
|
List []*struct {
|
||||||
|
SiteId string `json:"site_id"`
|
||||||
|
SiteName string `json:"site_name"`
|
||||||
|
Type string `json:"types"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
CreateTime int64 `json:"create_time"`
|
||||||
|
UpdateTime int64 `json:"update_time"`
|
||||||
|
} `json:"list"`
|
||||||
|
Total int32 `json:"total"`
|
||||||
|
} `json:"res,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SiteServerInfo struct {
|
||||||
|
ListenSSLPort *int32 `json:"listen_ssl_port,omitempty"`
|
||||||
|
SSL *SiteServerSSLInfo `json:"ssl,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SiteServerSSLInfo struct {
|
||||||
|
IsSSL *int32 `json:"is_ssl,omitempty"`
|
||||||
|
FullChain *string `json:"full_chain,omitempty"`
|
||||||
|
PrivateKey *string `json:"private_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifySiteRequest struct {
|
||||||
|
SiteId string `json:"site_id"`
|
||||||
|
Type *string `json:"types,omitempty"`
|
||||||
|
Server *SiteServerInfo `json:"server,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifySiteResponse struct {
|
||||||
|
baseResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigSetSSLRequest struct {
|
||||||
|
CertContent string `json:"certContent"`
|
||||||
|
KeyContent string `json:"keyContent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigSetSSLResponse struct {
|
||||||
|
baseResponse
|
||||||
|
}
|
@ -11,17 +11,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiToken string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiToken string) *Client {
|
func NewClient(apiToken string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL("https://api.bunny.net").
|
||||||
|
SetHeader("AccessKey", apiToken)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiToken: apiToken,
|
client: client,
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
|||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R()
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = "https://api.bunny.net" + path
|
|
||||||
req = req.SetHeader("AccessKey", c.apiToken)
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := make(map[string]string)
|
qs := make(map[string]string)
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -50,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
|
|
||||||
req = req.SetQueryParams(qs)
|
req = req.SetQueryParams(qs)
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := req.Send()
|
resp, err := req.Execute(method, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("bunny api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("bunny api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("bunny api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("bunny api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
@ -11,17 +11,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiToken string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiToken string) *Client {
|
func NewClient(apiToken string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL("https://api.cachefly.com/api/2.5").
|
||||||
|
SetHeader("x-cf-authorization", "Bearer "+apiToken)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiToken: apiToken,
|
client: client,
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
|||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R()
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = "https://api.cachefly.com/api/2.5" + path
|
|
||||||
req = req.SetHeader("x-cf-authorization", "Bearer "+c.apiToken)
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := make(map[string]string)
|
qs := make(map[string]string)
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -50,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
|
|
||||||
req = req.SetQueryParams(qs)
|
req = req.SetQueryParams(qs)
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := req.Send()
|
resp, err := req.Execute(method, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("cachefly api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("cachefly api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("cachefly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("cachefly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -75,7 +69,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("cachefly api error: failed to parse response: %w", err)
|
return fmt.Errorf("cachefly api error: failed to unmarshal response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -12,21 +12,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiHost string
|
|
||||||
apiKey string
|
|
||||||
apiSecret string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiHost, apiKey, apiSecret string) *Client {
|
func NewClient(apiHost, apiKey, apiSecret string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL(strings.TrimRight(apiHost, "/")).
|
||||||
|
SetHeader("api-key", apiKey).
|
||||||
|
SetHeader("api-secret", apiSecret)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiHost: strings.TrimRight(apiHost, "/"),
|
client: client,
|
||||||
apiKey: apiKey,
|
|
||||||
apiSecret: apiSecret,
|
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,11 +38,6 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
|||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R()
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = c.apiHost + path
|
|
||||||
req = req.
|
|
||||||
SetHeader("api-key", c.apiKey).
|
|
||||||
SetHeader("api-secret", c.apiSecret)
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := make(map[string]string)
|
qs := make(map[string]string)
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -62,16 +53,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
|
|
||||||
req = req.SetQueryParams(qs)
|
req = req.SetQueryParams(qs)
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := req.Send()
|
resp, err := req.Execute(method, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("cdnfly api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("cdnfly api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("cdnfly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("cdnfly api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -87,7 +76,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("cdnfly api error: failed to parse response: %w", err)
|
return fmt.Errorf("cdnfly api error: failed to unmarshal response: %w", err)
|
||||||
} else if errcode := result.GetCode(); errcode != "" && errcode != "0" {
|
} else if errcode := result.GetCode(); errcode != "" && errcode != "0" {
|
||||||
return fmt.Errorf("cdnfly api error: code='%s', message='%s'", errcode, result.GetMessage())
|
return fmt.Errorf("cdnfly api error: code='%s', message='%s'", errcode, result.GetMessage())
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiId string
|
|
||||||
apiSecret string
|
|
||||||
|
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiId, apiSecret string) *Client {
|
func NewClient(apiId, apiSecret string) *Client {
|
||||||
client := resty.New()
|
client := resty.New().
|
||||||
|
SetBaseURL("https://api.dns.la/api").
|
||||||
|
SetBasicAuth(apiId, apiSecret)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiId: apiId,
|
client: client,
|
||||||
apiSecret: apiSecret,
|
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||||
req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret)
|
req := c.client.R()
|
||||||
req.Method = method
|
|
||||||
req.URL = "https://api.dns.la/api" + path
|
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
qs := make(map[string]string)
|
qs := make(map[string]string)
|
||||||
if params != nil {
|
if params != nil {
|
||||||
@ -51,16 +46,14 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
|||||||
|
|
||||||
req = req.SetQueryParams(qs)
|
req = req.SetQueryParams(qs)
|
||||||
} else {
|
} else {
|
||||||
req = req.
|
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||||
SetHeader("Content-Type", "application/json").
|
|
||||||
SetBody(params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := req.Send()
|
resp, err := req.Execute(method, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("dnsla api error: failed to send request: %w", err)
|
return resp, fmt.Errorf("dnsla api error: failed to send request: %w", err)
|
||||||
} else if resp.IsError() {
|
} else if resp.IsError() {
|
||||||
return resp, fmt.Errorf("dnsla api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.Body())
|
return resp, fmt.Errorf("dnsla api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -76,7 +69,7 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
if err := json.Unmarshal(resp.Body(), &result); err != nil {
|
||||||
return fmt.Errorf("dnsla api error: failed to parse response: %w", err)
|
return fmt.Errorf("dnsla api error: failed to unmarshal response: %w", err)
|
||||||
} else if errcode := result.GetCode(); errcode/100 != 2 {
|
} else if errcode := result.GetCode(); errcode/100 != 2 {
|
||||||
return fmt.Errorf("dnsla api error: code='%d', message='%s'", errcode, result.GetMessage())
|
return fmt.Errorf("dnsla api error: code='%d', message='%s'", errcode, result.GetMessage())
|
||||||
}
|
}
|
||||||
|
@ -164,8 +164,8 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{}
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Add("Content-Type", mime)
|
req.Header.Set("Content-Type", mime)
|
||||||
req.Header.Add("Authorization", auth)
|
req.Header.Set("Authorization", auth)
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
@ -174,10 +174,10 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{}
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
r, err := io.ReadAll(resp.Body)
|
bytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return bytes, nil
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ func (c *EdgioClient) GetProperties(page int, pageSize int, organizationID strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.IsError() {
|
if resp.IsError() {
|
||||||
return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.Body())
|
return nil, fmt.Errorf("unexpected status code for getProperties: %d, %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &propertiesResp, nil
|
return &propertiesResp, nil
|
||||||
@ -512,7 +512,7 @@ func (c *EdgioClient) UploadCdnConfiguration(config *dtos.CDNConfiguration) (*dt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.IsError() {
|
if resp.IsError() {
|
||||||
return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.Body())
|
return nil, fmt.Errorf("unexpected status code for uploadCdnConfiguration: %d, %s", resp.StatusCode(), resp.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &response, nil
|
return &response, nil
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user