chore: move '/internal/pkg' to '/pkg'
This commit is contained in:
@@ -14,9 +14,9 @@ import (
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
xcert "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
xcert "github.com/usual2970/certimate/pkg/utils/cert"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
type acmeUser struct {
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
xslices "github.com/usual2970/certimate/internal/pkg/utils/slices"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
xslices "github.com/usual2970/certimate/pkg/utils/slices"
|
||||
)
|
||||
|
||||
type ApplyResult struct {
|
||||
|
||||
@@ -6,45 +6,45 @@ import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
pACMEHttpReq "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/acmehttpreq"
|
||||
pAliyun "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/aliyun"
|
||||
pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/aliyun-esa"
|
||||
pAWSRoute53 "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/aws-route53"
|
||||
pAzureDNS "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/azure-dns"
|
||||
pBaiduCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/baiducloud"
|
||||
pBunny "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/bunny"
|
||||
pCloudflare "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/cloudflare"
|
||||
pClouDNS "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/cloudns"
|
||||
pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/cmcccloud"
|
||||
pConstellix "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/constellix"
|
||||
pCTCCCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/ctcccloud"
|
||||
pDeSEC "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/desec"
|
||||
pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/digitalocean"
|
||||
pDNSLA "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/dnsla"
|
||||
pDuckDNS "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/duckdns"
|
||||
pDynv6 "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/dynv6"
|
||||
pGcore "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/gcore"
|
||||
pGname "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/gname"
|
||||
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/godaddy"
|
||||
pHetzner "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/hetzner"
|
||||
pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/huaweicloud"
|
||||
pJDCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/jdcloud"
|
||||
pNamecheap "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/namecheap"
|
||||
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/namedotcom"
|
||||
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/namesilo"
|
||||
pNetcup "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/netcup"
|
||||
pNetlify "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/netlify"
|
||||
pNS1 "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/ns1"
|
||||
pPorkbun "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/porkbun"
|
||||
pPowerDNS "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/powerdns"
|
||||
pRainYun "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/rainyun"
|
||||
pTencentCloud "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud"
|
||||
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud-eo"
|
||||
pUCloudUDNR "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/ucloud-udnr"
|
||||
pVercel "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/vercel"
|
||||
pVolcEngine "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/volcengine"
|
||||
pWestcn "github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/westcn"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
pACMEHttpReq "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/acmehttpreq"
|
||||
pAliyun "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/aliyun"
|
||||
pAliyunESA "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/aliyun-esa"
|
||||
pAWSRoute53 "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/aws-route53"
|
||||
pAzureDNS "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/azure-dns"
|
||||
pBaiduCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/baiducloud"
|
||||
pBunny "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/bunny"
|
||||
pCloudflare "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/cloudflare"
|
||||
pClouDNS "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/cloudns"
|
||||
pCMCCCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/cmcccloud"
|
||||
pConstellix "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/constellix"
|
||||
pCTCCCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ctcccloud"
|
||||
pDeSEC "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/desec"
|
||||
pDigitalOcean "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/digitalocean"
|
||||
pDNSLA "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/dnsla"
|
||||
pDuckDNS "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/duckdns"
|
||||
pDynv6 "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/dynv6"
|
||||
pGcore "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/gcore"
|
||||
pGname "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/gname"
|
||||
pGoDaddy "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/godaddy"
|
||||
pHetzner "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/hetzner"
|
||||
pHuaweiCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/huaweicloud"
|
||||
pJDCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/jdcloud"
|
||||
pNamecheap "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/namecheap"
|
||||
pNameDotCom "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/namedotcom"
|
||||
pNameSilo "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/namesilo"
|
||||
pNetcup "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/netcup"
|
||||
pNetlify "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/netlify"
|
||||
pNS1 "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ns1"
|
||||
pPorkbun "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/porkbun"
|
||||
pPowerDNS "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/powerdns"
|
||||
pRainYun "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/rainyun"
|
||||
pTencentCloud "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud"
|
||||
pTencentCloudEO "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud-eo"
|
||||
pUCloudUDNR "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/ucloud-udnr"
|
||||
pVercel "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/vercel"
|
||||
pVolcEngine "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/volcengine"
|
||||
pWestcn "github.com/usual2970/certimate/pkg/core/ssl-applicator/acme-dns01/providers/westcn"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
type applicantProviderOptions struct {
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/domain/dtos"
|
||||
"github.com/usual2970/certimate/internal/notify"
|
||||
xcert "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
xcert "github.com/usual2970/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
)
|
||||
|
||||
type Deployer interface {
|
||||
|
||||
@@ -6,105 +6,105 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
p1PanelConsole "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/1panel-console"
|
||||
p1PanelSite "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/1panel-site"
|
||||
pAliyunALB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-alb"
|
||||
pAliyunAPIGW "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-apigw"
|
||||
pAliyunCAS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-cas"
|
||||
pAliyunCASDeploy "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-cas-deploy"
|
||||
pAliyunCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-cdn"
|
||||
pAliyunCLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-clb"
|
||||
pAliyunDCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-dcdn"
|
||||
pAliyunDDoS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-ddos"
|
||||
pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-esa"
|
||||
pAliyunFC "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-fc"
|
||||
pAliyunGA "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-ga"
|
||||
pAliyunLive "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-live"
|
||||
pAliyunNLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-nlb"
|
||||
pAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-oss"
|
||||
pAliyunVOD "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-vod"
|
||||
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-waf"
|
||||
pAPISIX "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/apisix"
|
||||
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aws-acm"
|
||||
pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aws-cloudfront"
|
||||
pAWSIAM "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aws-iam"
|
||||
pAzureKeyVault "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/azure-keyvault"
|
||||
pBaiduCloudAppBLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baiducloud-appblb"
|
||||
pBaiduCloudBLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baiducloud-blb"
|
||||
pBaiduCloudCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baiducloud-cdn"
|
||||
pBaiduCloudCert "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baiducloud-cert"
|
||||
pBaishanCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baishan-cdn"
|
||||
pBaotaPanelConsole "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baotapanel-console"
|
||||
pBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baotapanel-site"
|
||||
pBaotaWAFConsole "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baotawaf-console"
|
||||
pBaotaWAFSite "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/baotawaf-site"
|
||||
pBunnyCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/bunny-cdn"
|
||||
pBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/byteplus-cdn"
|
||||
pCacheFly "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/cachefly"
|
||||
pCdnfly "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/cdnfly"
|
||||
pCTCCCloudAO "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-ao"
|
||||
pCTCCCloudCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-cdn"
|
||||
pCTCCCloudCMS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-cms"
|
||||
pCTCCCloudELB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-elb"
|
||||
pCTCCCloudICDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-icdn"
|
||||
pCTCCCloudLVDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ctcccloud-lvdn"
|
||||
pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/dogecloud-cdn"
|
||||
pEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/edgio-applications"
|
||||
pFlexCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/flexcdn"
|
||||
pGcoreCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/gcore-cdn"
|
||||
pGoEdge "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/goedge"
|
||||
pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/huaweicloud-cdn"
|
||||
pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/huaweicloud-elb"
|
||||
pHuaweiCloudSCM "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/huaweicloud-scm"
|
||||
pHuaweiCloudWAF "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/huaweicloud-waf"
|
||||
pJDCloudALB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/jdcloud-alb"
|
||||
pJDCloudCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/jdcloud-cdn"
|
||||
pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/jdcloud-live"
|
||||
pJDCloudVOD "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/jdcloud-vod"
|
||||
pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/k8s-secret"
|
||||
pLeCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/lecdn"
|
||||
pLocal "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/local"
|
||||
pNetlifySite "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/netlify-site"
|
||||
pProxmoxVE "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/proxmoxve"
|
||||
pQiniuCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/qiniu-cdn"
|
||||
pQiniuPili "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/qiniu-pili"
|
||||
pRainYunRCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/rainyun-rcdn"
|
||||
pRatPanelConsole "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ratpanel-console"
|
||||
pRatPanelSite "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ratpanel-site"
|
||||
pSafeLine "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/safeline"
|
||||
pSSH "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ssh"
|
||||
pTencentCloudCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-cdn"
|
||||
pTencentCloudCLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-clb"
|
||||
pTencentCloudCOS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-cos"
|
||||
pTencentCloudCSS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-css"
|
||||
pTencentCloudECDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-ecdn"
|
||||
pTencentCloudEO "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-eo"
|
||||
pTencentCloudGAAP "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-gaap"
|
||||
pTencentCloudSCF "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-scf"
|
||||
pTencentCloudSSL "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-ssl"
|
||||
pTencentCloudSSLDeploy "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-ssl-deploy"
|
||||
pTencentCloudVOD "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-vod"
|
||||
pTencentCloudWAF "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/tencentcloud-waf"
|
||||
pUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ucloud-ucdn"
|
||||
pUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/ucloud-us3"
|
||||
pUniCloudWebHost "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/unicloud-webhost"
|
||||
pUpyunCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/upyun-cdn"
|
||||
pVolcEngineALB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-alb"
|
||||
pVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-cdn"
|
||||
pVolcEngineCertCenter "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-certcenter"
|
||||
pVolcEngineCLB "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-clb"
|
||||
pVolcEngineDCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-dcdn"
|
||||
pVolcEngineImageX "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-imagex"
|
||||
pVolcEngineLive "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-live"
|
||||
pVolcEngineTOS "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/volcengine-tos"
|
||||
pWangsuCDN "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/wangsu-cdn"
|
||||
pWangsuCDNPro "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/wangsu-cdnpro"
|
||||
pWangsuCertificate "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/wangsu-certificate"
|
||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/webhook"
|
||||
xhttp "github.com/usual2970/certimate/internal/pkg/utils/http"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
xslices "github.com/usual2970/certimate/internal/pkg/utils/slices"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
p1PanelConsole "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/1panel-console"
|
||||
p1PanelSite "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/1panel-site"
|
||||
pAliyunALB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-alb"
|
||||
pAliyunAPIGW "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-apigw"
|
||||
pAliyunCAS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-cas"
|
||||
pAliyunCASDeploy "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-cas-deploy"
|
||||
pAliyunCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-cdn"
|
||||
pAliyunCLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-clb"
|
||||
pAliyunDCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-dcdn"
|
||||
pAliyunDDoS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-ddos"
|
||||
pAliyunESA "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-esa"
|
||||
pAliyunFC "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-fc"
|
||||
pAliyunGA "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-ga"
|
||||
pAliyunLive "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-live"
|
||||
pAliyunNLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-nlb"
|
||||
pAliyunOSS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-oss"
|
||||
pAliyunVOD "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-vod"
|
||||
pAliyunWAF "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aliyun-waf"
|
||||
pAPISIX "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/apisix"
|
||||
pAWSACM "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aws-acm"
|
||||
pAWSCloudFront "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aws-cloudfront"
|
||||
pAWSIAM "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/aws-iam"
|
||||
pAzureKeyVault "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/azure-keyvault"
|
||||
pBaiduCloudAppBLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baiducloud-appblb"
|
||||
pBaiduCloudBLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baiducloud-blb"
|
||||
pBaiduCloudCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baiducloud-cdn"
|
||||
pBaiduCloudCert "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baiducloud-cert"
|
||||
pBaishanCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baishan-cdn"
|
||||
pBaotaPanelConsole "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baotapanel-console"
|
||||
pBaotaPanelSite "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baotapanel-site"
|
||||
pBaotaWAFConsole "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baotawaf-console"
|
||||
pBaotaWAFSite "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/baotawaf-site"
|
||||
pBunnyCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/bunny-cdn"
|
||||
pBytePlusCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/byteplus-cdn"
|
||||
pCacheFly "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/cachefly"
|
||||
pCdnfly "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/cdnfly"
|
||||
pCTCCCloudAO "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-ao"
|
||||
pCTCCCloudCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-cdn"
|
||||
pCTCCCloudCMS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-cms"
|
||||
pCTCCCloudELB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-elb"
|
||||
pCTCCCloudICDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-icdn"
|
||||
pCTCCCloudLVDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ctcccloud-lvdn"
|
||||
pDogeCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/dogecloud-cdn"
|
||||
pEdgioApplications "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/edgio-applications"
|
||||
pFlexCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/flexcdn"
|
||||
pGcoreCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/gcore-cdn"
|
||||
pGoEdge "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/goedge"
|
||||
pHuaweiCloudCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/huaweicloud-cdn"
|
||||
pHuaweiCloudELB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/huaweicloud-elb"
|
||||
pHuaweiCloudSCM "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/huaweicloud-scm"
|
||||
pHuaweiCloudWAF "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/huaweicloud-waf"
|
||||
pJDCloudALB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/jdcloud-alb"
|
||||
pJDCloudCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/jdcloud-cdn"
|
||||
pJDCloudLive "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/jdcloud-live"
|
||||
pJDCloudVOD "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/jdcloud-vod"
|
||||
pK8sSecret "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/k8s-secret"
|
||||
pLeCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/lecdn"
|
||||
pLocal "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/local"
|
||||
pNetlifySite "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/netlify-site"
|
||||
pProxmoxVE "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/proxmoxve"
|
||||
pQiniuCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/qiniu-cdn"
|
||||
pQiniuPili "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/qiniu-pili"
|
||||
pRainYunRCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/rainyun-rcdn"
|
||||
pRatPanelConsole "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ratpanel-console"
|
||||
pRatPanelSite "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ratpanel-site"
|
||||
pSafeLine "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/safeline"
|
||||
pSSH "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ssh"
|
||||
pTencentCloudCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-cdn"
|
||||
pTencentCloudCLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-clb"
|
||||
pTencentCloudCOS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-cos"
|
||||
pTencentCloudCSS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-css"
|
||||
pTencentCloudECDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-ecdn"
|
||||
pTencentCloudEO "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-eo"
|
||||
pTencentCloudGAAP "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-gaap"
|
||||
pTencentCloudSCF "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-scf"
|
||||
pTencentCloudSSL "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-ssl"
|
||||
pTencentCloudSSLDeploy "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-ssl-deploy"
|
||||
pTencentCloudVOD "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-vod"
|
||||
pTencentCloudWAF "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/tencentcloud-waf"
|
||||
pUCloudUCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ucloud-ucdn"
|
||||
pUCloudUS3 "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/ucloud-us3"
|
||||
pUniCloudWebHost "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/unicloud-webhost"
|
||||
pUpyunCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/upyun-cdn"
|
||||
pVolcEngineALB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-alb"
|
||||
pVolcEngineCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-cdn"
|
||||
pVolcEngineCertCenter "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-certcenter"
|
||||
pVolcEngineCLB "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-clb"
|
||||
pVolcEngineDCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-dcdn"
|
||||
pVolcEngineImageX "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-imagex"
|
||||
pVolcEngineLive "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-live"
|
||||
pVolcEngineTOS "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/volcengine-tos"
|
||||
pWangsuCDN "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/wangsu-cdn"
|
||||
pWangsuCDNPro "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/wangsu-cdnpro"
|
||||
pWangsuCertificate "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/wangsu-certificate"
|
||||
pWebhook "github.com/usual2970/certimate/pkg/core/ssl-deployer/providers/webhook"
|
||||
xhttp "github.com/usual2970/certimate/pkg/utils/http"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
xslices "github.com/usual2970/certimate/pkg/utils/slices"
|
||||
)
|
||||
|
||||
type deployerProviderOptions struct {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xcert "github.com/usual2970/certimate/internal/pkg/utils/cert"
|
||||
xcert "github.com/usual2970/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
const CollectionNameCertificate = "certificate"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain/expr"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
const CollectionNameWorkflow = "workflow"
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
)
|
||||
|
||||
type Notifier interface {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
"github.com/usual2970/certimate/internal/repository"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
// Deprecated: v0.4.x 将废弃
|
||||
|
||||
@@ -5,18 +5,18 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||
pDiscordBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot"
|
||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||
pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||
pSlackBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot"
|
||||
pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||
pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||
xhttp "github.com/usual2970/certimate/internal/pkg/utils/http"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
pDingTalkBot "github.com/usual2970/certimate/pkg/core/notifier/providers/dingtalkbot"
|
||||
pDiscordBot "github.com/usual2970/certimate/pkg/core/notifier/providers/discordbot"
|
||||
pEmail "github.com/usual2970/certimate/pkg/core/notifier/providers/email"
|
||||
pLarkBot "github.com/usual2970/certimate/pkg/core/notifier/providers/larkbot"
|
||||
pMattermost "github.com/usual2970/certimate/pkg/core/notifier/providers/mattermost"
|
||||
pSlackBot "github.com/usual2970/certimate/pkg/core/notifier/providers/slackbot"
|
||||
pTelegramBot "github.com/usual2970/certimate/pkg/core/notifier/providers/telegrambot"
|
||||
pWebhook "github.com/usual2970/certimate/pkg/core/notifier/providers/webhook"
|
||||
pWeComBot "github.com/usual2970/certimate/pkg/core/notifier/providers/wecombot"
|
||||
xhttp "github.com/usual2970/certimate/pkg/utils/http"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
type notifierProviderOptions struct {
|
||||
|
||||
@@ -4,20 +4,20 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
pBark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
||||
pDingTalk "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
||||
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||
pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
||||
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
||||
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
||||
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||
pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||
pWeCom "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||
xmaps "github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||
"github.com/usual2970/certimate/pkg/core"
|
||||
pBark "github.com/usual2970/certimate/pkg/core/notifier/providers/bark"
|
||||
pDingTalk "github.com/usual2970/certimate/pkg/core/notifier/providers/dingtalkbot"
|
||||
pEmail "github.com/usual2970/certimate/pkg/core/notifier/providers/email"
|
||||
pGotify "github.com/usual2970/certimate/pkg/core/notifier/providers/gotify"
|
||||
pLark "github.com/usual2970/certimate/pkg/core/notifier/providers/larkbot"
|
||||
pMattermost "github.com/usual2970/certimate/pkg/core/notifier/providers/mattermost"
|
||||
pPushover "github.com/usual2970/certimate/pkg/core/notifier/providers/pushover"
|
||||
pPushPlus "github.com/usual2970/certimate/pkg/core/notifier/providers/pushplus"
|
||||
pServerChan "github.com/usual2970/certimate/pkg/core/notifier/providers/serverchan"
|
||||
pTelegram "github.com/usual2970/certimate/pkg/core/notifier/providers/telegrambot"
|
||||
pWebhook "github.com/usual2970/certimate/pkg/core/notifier/providers/webhook"
|
||||
pWeCom "github.com/usual2970/certimate/pkg/core/notifier/providers/wecombot"
|
||||
xmaps "github.com/usual2970/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
// Deprecated: v0.4.x 将废弃
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
type WithLogger interface {
|
||||
SetLogger(logger *slog.Logger)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// 表示定义消息通知器的抽象类型接口。
|
||||
type Notifier interface {
|
||||
WithLogger
|
||||
|
||||
// 发送通知。
|
||||
//
|
||||
// 入参:
|
||||
// - ctx:上下文。
|
||||
// - subject:通知主题。
|
||||
// - message:通知内容。
|
||||
//
|
||||
// 出参:
|
||||
// - res:发送结果。
|
||||
// - err: 错误。
|
||||
Notify(ctx context.Context, subject string, message string) (_res *NotifyResult, _err error)
|
||||
}
|
||||
|
||||
// 表示通知发送结果的数据结构。
|
||||
type NotifyResult struct {
|
||||
ExtendedData map[string]any `json:"extendedData,omitempty"`
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package bark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Bark 服务地址。
|
||||
// 零值时使用官方服务器。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// Bark 设备密钥。
|
||||
DeviceKey string `json:"deviceKey"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
const defaultServerURL = "https://api.day.app/"
|
||||
serverUrl := defaultServerURL
|
||||
if n.config.ServerUrl != "" {
|
||||
serverUrl = n.config.ServerUrl
|
||||
}
|
||||
|
||||
// REF: https://bark.day.app/#/tutorial
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
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 {
|
||||
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 &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package bark_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/bark"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fServerUrl string
|
||||
fDeviceKey string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_BARK_"
|
||||
|
||||
flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
|
||||
flag.StringVar(&fDeviceKey, argsPrefix+"DEVICEKEY", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./bark_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_BARK_SERVERURL="https://example.com/your-server-url" \
|
||||
--CERTIMATE_NOTIFIER_BARK_DEVICEKEY="your-device-key"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("SERVERURL: %v", fServerUrl),
|
||||
fmt.Sprintf("DEVICEKEY: %v", fDeviceKey),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
DeviceKey: fDeviceKey,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package dingtalkbot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
|
||||
"github.com/blinkbean/dingtalk"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// 钉钉机器人的 Webhook 地址。
|
||||
WebhookUrl string `json:"webhookUrl"`
|
||||
// 钉钉机器人的 Secret。
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dingtalk api error: invalid webhook url: %w", err)
|
||||
}
|
||||
|
||||
var bot *dingtalk.DingTalk
|
||||
if n.config.Secret == "" {
|
||||
bot = dingtalk.InitDingTalk([]string{webhookUrl.Query().Get("access_token")}, "")
|
||||
} else {
|
||||
bot = dingtalk.InitDingTalkWithSecret(webhookUrl.Query().Get("access_token"), n.config.Secret)
|
||||
}
|
||||
|
||||
if err := bot.SendTextMessage(subject + "\n" + message); err != nil {
|
||||
return nil, fmt.Errorf("dingtalk api error: %w", err)
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package dingtalkbot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fWebhookUrl string
|
||||
fSecret string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_DINGTALKBOT_"
|
||||
|
||||
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||
flag.StringVar(&fSecret, argsPrefix+"SECRET", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./dingtalkbot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_DINGTALKBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
|
||||
--CERTIMATE_NOTIFIER_DINGTALKBOT_SECRET="your-secret"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
||||
fmt.Sprintf("SECRET: %v", fSecret),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
WebhookUrl: fWebhookUrl,
|
||||
Secret: fSecret,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package discordbot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Discord Bot API Token。
|
||||
BotToken string `json:"botToken"`
|
||||
// Discord Channel ID。
|
||||
ChannelId string `json:"channelId"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://discord.com/developers/docs/resources/message#create-message
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Authorization", "Bot "+n.config.BotToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"content": subject + "\n" + message,
|
||||
})
|
||||
resp, err := req.Post(fmt.Sprintf("https://discord.com/api/v9/channels/%s/messages", n.config.ChannelId))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("discord api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("discord api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package discordbot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fApiToken string
|
||||
fChannelId string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_DISCORDBOT_"
|
||||
|
||||
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||
flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./discordbot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_DISCORDBOT_APITOKEN="your-bot-token" \
|
||||
--CERTIMATE_NOTIFIER_DISCORDBOT_CHANNELID="your-channel-id"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("APITOKEN: %v", fApiToken),
|
||||
fmt.Sprintf("CHANNELID: %v", fChannelId),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
BotToken: fApiToken,
|
||||
ChannelId: fChannelId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
|
||||
"github.com/domodwyer/mailyak/v3"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// SMTP 服务器地址。
|
||||
SmtpHost string `json:"smtpHost"`
|
||||
// SMTP 服务器端口。
|
||||
// 零值时根据是否启用 TLS 决定。
|
||||
SmtpPort int32 `json:"smtpPort"`
|
||||
// 是否启用 TLS。
|
||||
SmtpTls bool `json:"smtpTls"`
|
||||
// 用户名。
|
||||
Username string `json:"username"`
|
||||
// 密码。
|
||||
Password string `json:"password"`
|
||||
// 发件人邮箱。
|
||||
SenderAddress string `json:"senderAddress"`
|
||||
// 发件人显示名称。
|
||||
SenderName string `json:"senderName,omitempty"`
|
||||
// 收件人邮箱。
|
||||
ReceiverAddress string `json:"receiverAddress"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
var smtpAuth smtp.Auth
|
||||
if n.config.Username != "" || n.config.Password != "" {
|
||||
smtpAuth = smtp.PlainAuth("", n.config.Username, n.config.Password, n.config.SmtpHost)
|
||||
}
|
||||
|
||||
var smtpAddr string
|
||||
if n.config.SmtpPort == 0 {
|
||||
if n.config.SmtpTls {
|
||||
smtpAddr = net.JoinHostPort(n.config.SmtpHost, "465")
|
||||
} else {
|
||||
smtpAddr = net.JoinHostPort(n.config.SmtpHost, "25")
|
||||
}
|
||||
} else {
|
||||
smtpAddr = net.JoinHostPort(n.config.SmtpHost, strconv.Itoa(int(n.config.SmtpPort)))
|
||||
}
|
||||
|
||||
var yak *mailyak.MailYak
|
||||
if n.config.SmtpTls {
|
||||
yakWithTls, err := mailyak.NewWithTLS(smtpAddr, smtpAuth, newTlsConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yak = yakWithTls
|
||||
} else {
|
||||
yak = mailyak.New(smtpAddr, smtpAuth)
|
||||
}
|
||||
|
||||
yak.From(n.config.SenderAddress)
|
||||
yak.FromName(n.config.SenderName)
|
||||
yak.To(n.config.ReceiverAddress)
|
||||
yak.Subject(subject)
|
||||
yak.Plain().Set(message)
|
||||
|
||||
if err := yak.Send(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
|
||||
func newTlsConfig() *tls.Config {
|
||||
var suiteIds []uint16
|
||||
for _, suite := range tls.CipherSuites() {
|
||||
suiteIds = append(suiteIds, suite.ID)
|
||||
}
|
||||
for _, suite := range tls.InsecureCipherSuites() {
|
||||
suiteIds = append(suiteIds, suite.ID)
|
||||
}
|
||||
|
||||
// 为兼容国内部分低版本 TLS 的 SMTP 服务商
|
||||
return &tls.Config{
|
||||
MinVersion: tls.VersionTLS10,
|
||||
CipherSuites: suiteIds,
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package email_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fSmtpHost string
|
||||
fSmtpPort int64
|
||||
fSmtpTLS bool
|
||||
fUsername string
|
||||
fPassword string
|
||||
fSenderAddress string
|
||||
fReceiverAddress string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_EMAIL_"
|
||||
|
||||
flag.StringVar(&fSmtpHost, argsPrefix+"SMTPHOST", "", "")
|
||||
flag.Int64Var(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "")
|
||||
flag.BoolVar(&fSmtpTLS, argsPrefix+"SMTPTLS", false, "")
|
||||
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
|
||||
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
|
||||
flag.StringVar(&fSenderAddress, argsPrefix+"SENDERADDRESS", "", "")
|
||||
flag.StringVar(&fReceiverAddress, argsPrefix+"RECEIVERADDRESS", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./email_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_SMTPHOST="smtp.example.com" \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_SMTPPORT=465 \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_SMTPTLS=true \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_USERNAME="your-username" \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_PASSWORD="your-password" \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_SENDERADDRESS="sender@example.com" \
|
||||
--CERTIMATE_NOTIFIER_EMAIL_RECEIVERADDRESS="receiver@example.com"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("SMTPHOST: %v", fSmtpHost),
|
||||
fmt.Sprintf("SMTPPORT: %v", fSmtpPort),
|
||||
fmt.Sprintf("SMTPTLS: %v", fSmtpTLS),
|
||||
fmt.Sprintf("USERNAME: %v", fUsername),
|
||||
fmt.Sprintf("PASSWORD: %v", fPassword),
|
||||
fmt.Sprintf("SENDERADDRESS: %v", fSenderAddress),
|
||||
fmt.Sprintf("RECEIVERADDRESS: %v", fReceiverAddress),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
SmtpHost: fSmtpHost,
|
||||
SmtpPort: int32(fSmtpPort),
|
||||
SmtpTls: fSmtpTLS,
|
||||
Username: fUsername,
|
||||
Password: fPassword,
|
||||
SenderAddress: fSenderAddress,
|
||||
ReceiverAddress: fReceiverAddress,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package gotify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Gotify 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// Gotify Token。
|
||||
Token string `json:"token"`
|
||||
// Gotify 消息优先级。
|
||||
Priority int64 `json:"priority,omitempty"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
|
||||
|
||||
// REF: https://gotify.net/api-docs#/message/createMessage
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Authorization", "Bearer "+n.config.Token).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"title": subject,
|
||||
"message": message,
|
||||
"priority": n.config.Priority,
|
||||
})
|
||||
resp, err := req.Post(fmt.Sprintf("%s/message", serverUrl))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gotify api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("gotify api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package gotify_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fUrl string
|
||||
fToken string
|
||||
fPriority int64
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_GOTIFY_"
|
||||
flag.StringVar(&fUrl, argsPrefix+"URL", "", "")
|
||||
flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "")
|
||||
flag.Int64Var(&fPriority, argsPrefix+"PRIORITY", 0, "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./gotify_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_GOTIFY_URL="https://example.com" \
|
||||
--CERTIMATE_NOTIFIER_GOTIFY_TOKEN="your-gotify-application-token" \
|
||||
--CERTIMATE_NOTIFIER_GOTIFY_PRIORITY="your-message-priority"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("URL: %v", fUrl),
|
||||
fmt.Sprintf("TOKEN: %v", fToken),
|
||||
fmt.Sprintf("PRIORITY: %d", fPriority),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
ServerUrl: fUrl,
|
||||
Token: fToken,
|
||||
Priority: fPriority,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package larkbot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-lark/lark"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// 飞书机器人 Webhook 地址。
|
||||
WebhookUrl string `json:"webhookUrl"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
bot := lark.NewNotificationBot(n.config.WebhookUrl)
|
||||
content := lark.NewPostBuilder().
|
||||
Title(subject).
|
||||
TextTag(message, 1, false).
|
||||
Render()
|
||||
msg := lark.NewMsgBuffer(lark.MsgPost).Post(content)
|
||||
resp, err := bot.PostNotificationV2(msg.Build())
|
||||
if err != nil {
|
||||
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 &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package larkbot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var fWebhookUrl string
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_LARKBOT_"
|
||||
|
||||
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./larkbot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_LARKBOT_WEBHOOKURL="https://example.com/your-webhook-url"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
WebhookUrl: fWebhookUrl,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package mattermost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Mattermost 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// Mattermost 用户名。
|
||||
Username string `json:"username"`
|
||||
// Mattermost 密码。
|
||||
Password string `json:"password"`
|
||||
// Mattermost 频道 ID。
|
||||
ChannelId string `json:"channelId"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
serverUrl := strings.TrimRight(n.config.ServerUrl, "/")
|
||||
|
||||
// REF: https://developers.mattermost.com/api-documentation/#/operations/Login
|
||||
loginReq := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
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")
|
||||
}
|
||||
|
||||
// REF: https://developers.mattermost.com/api-documentation/#/operations/CreatePost
|
||||
postReq := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Authorization", "Bearer "+loginResp.Header().Get("Token")).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"channel_id": n.config.ChannelId,
|
||||
"props": map[string]interface{}{
|
||||
"attachments": []map[string]interface{}{
|
||||
{
|
||||
"title": subject,
|
||||
"text": message,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
postResp, err := postReq.Post(fmt.Sprintf("%s/api/v4/posts", serverUrl))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err)
|
||||
} else if postResp.IsError() {
|
||||
return nil, fmt.Errorf("mattermost api error: unexpected status code: %d, resp: %s", postResp.StatusCode(), postResp.String())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package mattermost_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fServerUrl string
|
||||
fChannelId string
|
||||
fUsername string
|
||||
fPassword string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_MATTERMOST_"
|
||||
|
||||
flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
|
||||
flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", "", "")
|
||||
flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
|
||||
flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./mattermost_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_MATTERMOST_SERVERURL="https://example.com/your-server-url" \
|
||||
--CERTIMATE_NOTIFIER_MATTERMOST_CHANNELID="your-chanel-id" \
|
||||
--CERTIMATE_NOTIFIER_MATTERMOST_USERNAME="your-username" \
|
||||
--CERTIMATE_NOTIFIER_MATTERMOST_PASSWORD="your-password"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("SERVERURL: %v", fServerUrl),
|
||||
fmt.Sprintf("CHANNELID: %v", fChannelId),
|
||||
fmt.Sprintf("USERNAME: %v", fUsername),
|
||||
fmt.Sprintf("PASSWORD: %v", fPassword),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
ChannelId: fChannelId,
|
||||
Username: fUsername,
|
||||
Password: fPassword,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package pushover
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Pushover API Token。
|
||||
Token string `json:"token"`
|
||||
// 用户或分组标识。
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://pushover.net/api
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"title": subject,
|
||||
"message": message,
|
||||
"token": n.config.Token,
|
||||
"user": n.config.User,
|
||||
})
|
||||
resp, err := req.Post("https://api.pushover.net/1/messages.json")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pushover api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("pushover api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package pushover_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fToken string
|
||||
fUser string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_PUSHOVER_"
|
||||
flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "")
|
||||
flag.StringVar(&fUser, argsPrefix+"USER", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./pushover_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_PUSHOVER_TOKEN="your-pushover-token" \
|
||||
--CERTIMATE_NOTIFIER_PUSHOVER_USER="your-pushover-user" \
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("TOKEN: %v", fToken),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
Token: fToken,
|
||||
User: fUser,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package pushplus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// PushPlus Token。
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// 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
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"title": subject,
|
||||
"content": message,
|
||||
"token": n.config.Token,
|
||||
})
|
||||
resp, err := req.Post("https://www.pushplus.plus/send")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("pushplus api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
var errorResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
if err := json.Unmarshal(resp.Body(), &errorResponse); err != nil {
|
||||
return nil, fmt.Errorf("pushplus api error: failed to unmarshal response: %w", err)
|
||||
} else if errorResponse.Code != 200 {
|
||||
return nil, fmt.Errorf("pushplus api error: code='%d', message='%s'", errorResponse.Code, errorResponse.Message)
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package pushplus_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var fToken string
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_PUSHPLUS_"
|
||||
flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./pushplus_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_PUSHPLUS_TOKEN="your-pushplus-token" \
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("TOKEN: %v", fToken),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
Token: fToken,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package serverchan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// ServerChan 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://sct.ftqq.com/
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"text": subject,
|
||||
"desp": message,
|
||||
})
|
||||
resp, err := req.Post(n.config.ServerUrl)
|
||||
if err != nil {
|
||||
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 &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package serverchan_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var fUrl string
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_SERVERCHAN_"
|
||||
|
||||
flag.StringVar(&fUrl, argsPrefix+"URL", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./serverchan_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_SERVERCHAN_URL="https://example.com/your-webhook-url" \
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("URL: %v", fUrl),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
ServerUrl: fUrl,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package discordbot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Slack Bot API Token。
|
||||
BotToken string `json:"botToken"`
|
||||
// Slack Channel ID。
|
||||
ChannelId string `json:"channelId"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://docs.slack.dev/messaging/sending-and-scheduling-messages#publishing
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Authorization", "Bearer "+n.config.BotToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"token": n.config.BotToken,
|
||||
"channel": n.config.ChannelId,
|
||||
"text": subject + "\n" + message,
|
||||
})
|
||||
resp, err := req.Post("https://slack.com/api/chat.postMessage")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("slack api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("slack api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package discordbot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fApiToken string
|
||||
fChannelId string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_SLACKBOT_"
|
||||
|
||||
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||
flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./slackbot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_SLACKBOT_APITOKEN="your-bot-token" \
|
||||
--CERTIMATE_NOTIFIER_SLACKBOT_CHANNELID="your-channel-id"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("APITOKEN: %v", fApiToken),
|
||||
fmt.Sprintf("CHANNELID: %v", fChannelId),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
BotToken: fApiToken,
|
||||
ChannelId: fChannelId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package telegrambot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Telegram Bot API Token。
|
||||
BotToken string `json:"botToken"`
|
||||
// Telegram Chat ID。
|
||||
ChatId int64 `json:"chatId"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://core.telegram.org/bots/api#sendmessage
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
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 {
|
||||
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())
|
||||
}
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package telegrambot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fApiToken string
|
||||
fChatId int64
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAMBOT_"
|
||||
|
||||
flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "")
|
||||
flag.Int64Var(&fChatId, argsPrefix+"CHATID", 0, "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./telegrambot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_TELEGRAMBOT_APITOKEN="your-api-token" \
|
||||
--CERTIMATE_NOTIFIER_TELEGRAMBOT_CHATID=123456
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("APITOKEN: %v", fApiToken),
|
||||
fmt.Sprintf("CHATID: %v", fChatId),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
BotToken: fApiToken,
|
||||
ChatId: fChatId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// Webhook URL。
|
||||
WebhookUrl string `json:"webhookUrl"`
|
||||
// Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。
|
||||
WebhookData string `json:"webhookData,omitempty"`
|
||||
// 请求谓词。
|
||||
// 零值时默认值 "POST"。
|
||||
Method string `json:"method,omitempty"`
|
||||
// 请求标头。
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New().
|
||||
SetTimeout(30 * time.Second).
|
||||
SetRetryCount(3).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
if config.AllowInsecureConnections {
|
||||
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// 处理 Webhook URL
|
||||
webhookUrl, err := url.Parse(n.config.WebhookUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse webhook url: %w", err)
|
||||
} else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" {
|
||||
return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme)
|
||||
}
|
||||
|
||||
// 处理 Webhook 请求谓词
|
||||
webhookMethod := strings.ToUpper(n.config.Method)
|
||||
if webhookMethod == "" {
|
||||
webhookMethod = http.MethodPost
|
||||
} else if webhookMethod != http.MethodGet &&
|
||||
webhookMethod != http.MethodPost &&
|
||||
webhookMethod != http.MethodPut &&
|
||||
webhookMethod != http.MethodPatch &&
|
||||
webhookMethod != http.MethodDelete {
|
||||
return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod)
|
||||
}
|
||||
|
||||
// 处理 Webhook 请求标头
|
||||
webhookHeaders := make(http.Header)
|
||||
for k, v := range n.config.Headers {
|
||||
webhookHeaders.Set(k, v)
|
||||
}
|
||||
|
||||
// 处理 Webhook 请求内容类型
|
||||
const CONTENT_TYPE_JSON = "application/json"
|
||||
const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"
|
||||
const CONTENT_TYPE_MULTIPART = "multipart/form-data"
|
||||
webhookContentType := webhookHeaders.Get("Content-Type")
|
||||
if webhookContentType == "" {
|
||||
webhookContentType = CONTENT_TYPE_JSON
|
||||
webhookHeaders.Set("Content-Type", CONTENT_TYPE_JSON)
|
||||
} else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) &&
|
||||
strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) &&
|
||||
strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) {
|
||||
return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType)
|
||||
}
|
||||
|
||||
// 处理 Webhook 请求数据
|
||||
var webhookData interface{}
|
||||
if n.config.WebhookData == "" {
|
||||
webhookData = map[string]string{
|
||||
"subject": subject,
|
||||
"message": message,
|
||||
}
|
||||
} else {
|
||||
err = json.Unmarshal([]byte(n.config.WebhookData), &webhookData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||
}
|
||||
|
||||
replaceJsonValueRecursively(webhookData, "${SUBJECT}", subject)
|
||||
replaceJsonValueRecursively(webhookData, "${MESSAGE}", message)
|
||||
|
||||
if webhookMethod == http.MethodGet || webhookContentType == CONTENT_TYPE_FORM || webhookContentType == CONTENT_TYPE_MULTIPART {
|
||||
temp := make(map[string]string)
|
||||
jsonb, err := json.Marshal(webhookData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||
} else if err := json.Unmarshal(jsonb, &temp); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err)
|
||||
} else {
|
||||
webhookData = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成请求
|
||||
// 其中 GET 请求需转换为查询参数
|
||||
req := n.httpClient.R().SetContext(ctx).SetHeaderMultiValues(webhookHeaders)
|
||||
req.URL = webhookUrl.String()
|
||||
req.Method = webhookMethod
|
||||
if webhookMethod == http.MethodGet {
|
||||
req.SetQueryParams(webhookData.(map[string]string))
|
||||
} else {
|
||||
switch webhookContentType {
|
||||
case CONTENT_TYPE_JSON:
|
||||
req.SetBody(webhookData)
|
||||
case CONTENT_TYPE_FORM:
|
||||
req.SetFormData(webhookData.(map[string]string))
|
||||
case CONTENT_TYPE_MULTIPART:
|
||||
req.SetMultipartFormData(webhookData.(map[string]string))
|
||||
}
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
resp, err := req.Send()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("webhook error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return nil, fmt.Errorf("webhook error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
n.logger.Debug("webhook responded", slog.String("response", resp.String()))
|
||||
|
||||
return &core.NotifyResult{}, nil
|
||||
}
|
||||
|
||||
func replaceJsonValueRecursively(data interface{}, oldStr, newStr string) interface{} {
|
||||
switch v := data.(type) {
|
||||
case map[string]any:
|
||||
for k, val := range v {
|
||||
v[k] = replaceJsonValueRecursively(val, oldStr, newStr)
|
||||
}
|
||||
case []any:
|
||||
for i, val := range v {
|
||||
v[i] = replaceJsonValueRecursively(val, oldStr, newStr)
|
||||
}
|
||||
case string:
|
||||
return strings.ReplaceAll(v, oldStr, newStr)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package webhook_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var (
|
||||
fWebhookUrl string
|
||||
fWebhookContentType string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_WEBHOOK_"
|
||||
|
||||
flag.StringVar(&fWebhookUrl, argsPrefix+"URL", "", "")
|
||||
flag.StringVar(&fWebhookContentType, argsPrefix+"CONTENTTYPE", "application/json", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./webhook_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_WEBHOOK_URL="https://example.com/your-webhook-url" \
|
||||
--CERTIMATE_NOTIFIER_WEBHOOK_CONTENTTYPE="application/json"
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("URL: %v", fWebhookUrl),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
WebhookUrl: fWebhookUrl,
|
||||
Method: "POST",
|
||||
Headers: map[string]string{
|
||||
"Content-Type": fWebhookContentType,
|
||||
},
|
||||
AllowInsecureConnections: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package wecombot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type NotifierProviderConfig struct {
|
||||
// 企业微信机器人 Webhook 地址。
|
||||
WebhookUrl string `json:"webhookUrl"`
|
||||
}
|
||||
|
||||
type NotifierProvider struct {
|
||||
config *NotifierProviderConfig
|
||||
logger *slog.Logger
|
||||
httpClient *resty.Client
|
||||
}
|
||||
|
||||
var _ core.Notifier = (*NotifierProvider)(nil)
|
||||
|
||||
func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
|
||||
return &NotifierProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
httpClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (*core.NotifyResult, error) {
|
||||
// REF: https://developer.work.weixin.qq.com/document/path/91770
|
||||
req := n.httpClient.R().
|
||||
SetContext(ctx).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", "certimate").
|
||||
SetBody(map[string]any{
|
||||
"msgtype": "text",
|
||||
"text": map[string]string{
|
||||
"content": subject + "\n\n" + message,
|
||||
},
|
||||
})
|
||||
resp, err := req.Post(n.config.WebhookUrl)
|
||||
if err != nil {
|
||||
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 &core.NotifyResult{}, nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package wecombot_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot"
|
||||
)
|
||||
|
||||
const (
|
||||
mockSubject = "test_subject"
|
||||
mockMessage = "test_message"
|
||||
)
|
||||
|
||||
var fWebhookUrl string
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_NOTIFIER_WECOMBOT_"
|
||||
|
||||
flag.StringVar(&fWebhookUrl, argsPrefix+"WEBHOOKURL", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./wecombot_test.go -args \
|
||||
--CERTIMATE_NOTIFIER_WECOMBOT_WEBHOOKURL="https://example.com/your-webhook-url" \
|
||||
*/
|
||||
func TestNotify(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("WEBHOOKURL: %v", fWebhookUrl),
|
||||
}, "\n"))
|
||||
|
||||
notifier, err := provider.NewNotifierProvider(&provider.NotifierProviderConfig{
|
||||
WebhookUrl: fWebhookUrl,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package acmehttpreq
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/httpreq"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
Mode string `json:"mode"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
endpoint, _ := url.Parse(config.Endpoint)
|
||||
providerConfig := httpreq.NewDefaultConfig()
|
||||
providerConfig.Endpoint = endpoint
|
||||
providerConfig.Mode = config.Mode
|
||||
providerConfig.Username = config.Username
|
||||
providerConfig.Password = config.Password
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := httpreq.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package aliyunesa
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/aliyun-esa/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.SecretID = config.AccessKeyId
|
||||
providerConfig.SecretKey = config.AccessKeySecret
|
||||
providerConfig.RegionID = config.Region
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
aliesa "github.com/alibabacloud-go/esa-20240910/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "ALICLOUDESA_"
|
||||
|
||||
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
EnvRegionID = envNamespace + "REGION_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
SecretID string
|
||||
SecretKey string
|
||||
RegionID string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *aliesa.Client
|
||||
config *Config
|
||||
|
||||
siteIDs map[string]int64
|
||||
siteIDsMtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKey, EnvSecretKey, EnvRegionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alicloud-esa: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.SecretID = values[EnvAccessKey]
|
||||
config.SecretKey = values[EnvSecretKey]
|
||||
config.RegionID = values[EnvRegionID]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("alicloud-esa: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := aliesa.NewClient(&aliopen.Config{
|
||||
AccessKeyId: tea.String(config.SecretID),
|
||||
AccessKeySecret: tea.String(config.SecretKey),
|
||||
Endpoint: tea.String(fmt.Sprintf("esa.%s.aliyuncs.com", config.RegionID)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alicloud-esa: %w", err)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
|
||||
siteIDs: make(map[string]int64),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
siteName := dns01.UnFqdn(authZone)
|
||||
siteId, err := d.getSiteId(siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("alicloud-esa: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
siteName := dns01.UnFqdn(authZone)
|
||||
siteId, err := d.getSiteId(siteName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(siteId, dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
|
||||
return fmt.Errorf("alicloud-esa: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getSiteId(siteName string) (int64, error) {
|
||||
d.siteIDsMtx.Lock()
|
||||
siteID, ok := d.siteIDs[siteName]
|
||||
d.siteIDsMtx.Unlock()
|
||||
if ok {
|
||||
return siteID, nil
|
||||
}
|
||||
|
||||
pageNumber := 1
|
||||
pageSize := 500
|
||||
for {
|
||||
request := &aliesa.ListSitesRequest{
|
||||
SiteName: tea.String(siteName),
|
||||
SiteSearchType: tea.String("exact"),
|
||||
PageNumber: tea.Int32(int32(pageNumber)),
|
||||
PageSize: tea.Int32(int32(pageSize)),
|
||||
AccessType: tea.String("NS"),
|
||||
}
|
||||
response, err := d.client.ListSites(request)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if response.Body == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Body.Sites {
|
||||
if tea.StringValue(record.SiteName) == siteName {
|
||||
d.siteIDsMtx.Lock()
|
||||
d.siteIDs[siteName] = *record.SiteId
|
||||
d.siteIDsMtx.Unlock()
|
||||
return *record.SiteId, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Body.Sites) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("site not found")
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(siteId int64, effectiveFQDN string) (*aliesa.ListRecordsResponseBodyRecords, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 500
|
||||
for {
|
||||
request := &aliesa.ListRecordsRequest{
|
||||
SiteId: tea.Int64(siteId),
|
||||
Type: tea.String("TXT"),
|
||||
RecordName: tea.String(effectiveFQDN),
|
||||
RecordMatchType: tea.String("exact"),
|
||||
PageNumber: tea.Int32(int32(pageNumber)),
|
||||
PageSize: tea.Int32(int32(pageSize)),
|
||||
}
|
||||
response, err := d.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Body == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Body.Records {
|
||||
if tea.StringValue(record.RecordName) == effectiveFQDN {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Body.Records) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(siteId int64, effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(siteId, effectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &aliesa.CreateRecordRequest{
|
||||
SiteId: tea.Int64(siteId),
|
||||
Type: tea.String("TXT"),
|
||||
RecordName: tea.String(effectiveFQDN),
|
||||
Data: &aliesa.CreateRecordRequestData{
|
||||
Value: tea.String(value),
|
||||
},
|
||||
Ttl: tea.Int32(d.config.TTL),
|
||||
}
|
||||
_, err := d.client.CreateRecord(request)
|
||||
return err
|
||||
} else {
|
||||
request := &aliesa.UpdateRecordRequest{
|
||||
RecordId: record.RecordId,
|
||||
Ttl: tea.Int32(d.config.TTL),
|
||||
Data: &aliesa.UpdateRecordRequestData{
|
||||
Value: tea.String(value),
|
||||
},
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(siteId int64, effectiveFQDN string) error {
|
||||
record, err := d.findDNSRecord(siteId, effectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := &aliesa.DeleteRecordRequest{
|
||||
RecordId: record.RecordId,
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package aliyun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := alidns.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.AccessKeyId
|
||||
providerConfig.SecretKey = config.AccessKeySecret
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := alidns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package awsroute53
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/route53"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
HostedZoneId string `json:"hostedZoneId"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := route53.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
providerConfig.Region = config.Region
|
||||
providerConfig.HostedZoneID = config.HostedZoneId
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := route53.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package azuredns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/azuredns"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
azenv "github.com/usual2970/certimate/internal/pkg/sdk3rd/azure/env"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
TenantId string `json:"tenantId"`
|
||||
ClientId string `json:"clientId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
CloudName string `json:"cloudName,omitempty"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := azuredns.NewDefaultConfig()
|
||||
providerConfig.TenantID = config.TenantId
|
||||
providerConfig.ClientID = config.ClientId
|
||||
providerConfig.ClientSecret = config.ClientSecret
|
||||
if config.CloudName != "" {
|
||||
env, err := azenv.GetCloudEnvConfiguration(config.CloudName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
providerConfig.Environment = env
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := azuredns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package baiducloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/baiducloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bcedns "github.com/baidubce/bce-sdk-go/services/dns"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "BAIDUCLOUD_"
|
||||
|
||||
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
|
||||
EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *bcedns.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKeyID = values[EnvAccessKeyID]
|
||||
config.SecretAccessKey = values[EnvSecretAccessKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("baiducloud: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := bcedns.NewClient(config.AccessKeyID, config.SecretAccessKey, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if client.Config != nil {
|
||||
client.Config.ConnectionTimeoutInMillis = int(config.HTTPTimeout.Milliseconds())
|
||||
}
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("baiducloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("baiducloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*bcedns.Record, error) {
|
||||
pageMarker := ""
|
||||
pageSize := 1000
|
||||
for {
|
||||
request := &bcedns.ListRecordRequest{}
|
||||
request.Rr = subDomain
|
||||
request.Marker = pageMarker
|
||||
request.MaxKeys = pageSize
|
||||
|
||||
response, err := d.client.ListRecord(zoneName, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Records {
|
||||
if record.Type == "TXT" && record.Rr == subDomain {
|
||||
return &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Records) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageMarker = response.NextMarker
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &bcedns.CreateRecordRequest{
|
||||
Type: "TXT",
|
||||
Rr: subDomain,
|
||||
Value: value,
|
||||
Ttl: &d.config.TTL,
|
||||
}
|
||||
err := d.client.CreateRecord(zoneName, request, d.generateClientToken())
|
||||
return err
|
||||
} else {
|
||||
request := &bcedns.UpdateRecordRequest{
|
||||
Type: "TXT",
|
||||
Rr: subDomain,
|
||||
Value: value,
|
||||
Ttl: &d.config.TTL,
|
||||
}
|
||||
err := d.client.UpdateRecord(zoneName, record.Id, request, d.generateClientToken())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
err = d.client.DeleteRecord(zoneName, record.Id, d.generateClientToken())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) generateClientToken() string {
|
||||
return strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package bunny
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/bunny"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := bunny.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := bunny.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
DnsApiToken string `json:"dnsApiToken"`
|
||||
ZoneApiToken string `json:"zoneApiToken,omitempty"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := cloudflare.NewDefaultConfig()
|
||||
providerConfig.AuthToken = config.DnsApiToken
|
||||
providerConfig.ZoneToken = config.ZoneApiToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := cloudflare.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cloudns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudns"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AuthId string `json:"authId"`
|
||||
AuthPassword string `json:"authPassword"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := cloudns.NewDefaultConfig()
|
||||
providerConfig.AuthID = config.AuthId
|
||||
providerConfig.AuthPassword = config.AuthPassword
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := cloudns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package cmcccloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/cmcccloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKey = config.AccessKeyId
|
||||
providerConfig.SecretKey = config.AccessKeySecret
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"gitlab.ecloud.com/ecloud/ecloudsdkclouddns"
|
||||
"gitlab.ecloud.com/ecloud/ecloudsdkclouddns/model"
|
||||
"gitlab.ecloud.com/ecloud/ecloudsdkcore/config"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "CMCCCLOUD_"
|
||||
|
||||
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvReadTimeOut = envNamespace + "READ_TIMEOUT"
|
||||
EnvConnectTimeout = envNamespace + "CONNECT_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
ReadTimeOut int
|
||||
ConnectTimeout int
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *ecloudsdkclouddns.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
ReadTimeOut: env.GetOrDefaultInt(EnvReadTimeOut, 30),
|
||||
ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30),
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 600)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKey, EnvSecretKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
cfg := NewDefaultConfig()
|
||||
cfg.AccessKey = values[EnvAccessKey]
|
||||
cfg.SecretKey = values[EnvSecretKey]
|
||||
|
||||
return NewDNSProviderConfig(cfg)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) {
|
||||
if cfg == nil {
|
||||
return nil, errors.New("cmccecloud: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client := ecloudsdkclouddns.NewClient(&config.Config{
|
||||
AccessKey: cfg.AccessKey,
|
||||
SecretKey: cfg.SecretKey,
|
||||
// 资源池常量见: https://ecloud.10086.cn/op-help-center/doc/article/54462
|
||||
// 默认全局
|
||||
PoolId: "CIDC-CORE-00",
|
||||
ReadTimeOut: cfg.ReadTimeOut,
|
||||
ConnectTimeout: cfg.ConnectTimeout,
|
||||
})
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
readDomain := strings.Trim(zoneName, ".")
|
||||
record, err := d.getDomainRecord(readDomain, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
resp, err := d.client.CreateRecordOpenapi(&model.CreateRecordOpenapiRequest{
|
||||
CreateRecordOpenapiBody: &model.CreateRecordOpenapiBody{
|
||||
LineId: "0", // 默认线路
|
||||
Rr: subDomain,
|
||||
DomainName: readDomain,
|
||||
Description: "certimate acme",
|
||||
Type: model.CreateRecordOpenapiBodyTypeEnumTxt,
|
||||
Value: info.Value,
|
||||
Ttl: &d.config.TTL,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
if resp.State != model.CreateRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: create record failed, response state: %s, message: %s, code: %s", resp.State, resp.ErrorMessage, resp.ErrorCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
resp, err := d.client.ModifyRecordOpenapi(&model.ModifyRecordOpenapiRequest{
|
||||
ModifyRecordOpenapiBody: &model.ModifyRecordOpenapiBody{
|
||||
RecordId: record.RecordId,
|
||||
Rr: subDomain,
|
||||
DomainName: readDomain,
|
||||
Description: "certmate acme",
|
||||
LineId: "0",
|
||||
Type: model.ModifyRecordOpenapiBodyTypeEnumTxt,
|
||||
Value: info.Value,
|
||||
Ttl: &d.config.TTL,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
if resp.State != model.ModifyRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: create record failed, response state: %s", resp.State)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
challengeInfo := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zoneName, err := dns01.FindZoneByFqdn(challengeInfo.FQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(challengeInfo.FQDN, zoneName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
|
||||
readDomain := strings.Trim(zoneName, ".")
|
||||
record, err := d.getDomainRecord(readDomain, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
resp, err := d.client.DeleteRecordOpenapi(&model.DeleteRecordOpenapiRequest{
|
||||
DeleteRecordOpenapiBody: &model.DeleteRecordOpenapiBody{
|
||||
RecordIdList: []string{record.RecordId},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cmccecloud: %w", err)
|
||||
}
|
||||
if resp.State != model.DeleteRecordOpenapiResponseStateEnumOk {
|
||||
return fmt.Errorf("cmccecloud: delete record failed, unexpected response state: %s", resp.State)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDomainRecord(domain string, rr string) (*model.ListRecordOpenapiResponseData, error) {
|
||||
pageSize := int32(50)
|
||||
page := int32(1)
|
||||
for {
|
||||
resp, err := d.client.ListRecordOpenapi(&model.ListRecordOpenapiRequest{
|
||||
ListRecordOpenapiBody: &model.ListRecordOpenapiBody{
|
||||
DomainName: domain,
|
||||
},
|
||||
ListRecordOpenapiQuery: &model.ListRecordOpenapiQuery{
|
||||
PageSize: &pageSize,
|
||||
Page: &page,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.State != model.ListRecordOpenapiResponseStateEnumOk {
|
||||
respStr, _ := json.Marshal(resp)
|
||||
return nil, fmt.Errorf("cmccecloud: request error: %s", string(respStr))
|
||||
}
|
||||
|
||||
if resp.Body.Data != nil {
|
||||
for _, item := range *resp.Body.Data {
|
||||
if item.Rr == rr {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resp.Body.TotalPages == nil || page >= *resp.Body.TotalPages {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cloudns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/constellix"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := constellix.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
providerConfig.SecretKey = config.SecretKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := constellix.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package ctcccloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/ctcccloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKeyId = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
|
||||
ctyundns "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/dns"
|
||||
xtypes "github.com/usual2970/certimate/internal/pkg/utils/types"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "CTYUNSMARTDNS_"
|
||||
|
||||
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
|
||||
EnvSecretAccessKey = envNamespace + "SECRET_ACCESS_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKeyId string
|
||||
SecretAccessKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *ctyundns.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 600),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKeyID, EnvSecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKeyId = values[EnvAccessKeyID]
|
||||
config.SecretAccessKey = values[EnvSecretAccessKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("ctyun: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := ctyundns.NewClient(config.AccessKeyId, config.SecretAccessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("ctyun: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecordId(zoneName, subDomain string) (int32, error) {
|
||||
// 查询解析记录列表
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11264&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.QueryRecordListRequest{}
|
||||
request.Domain = xtypes.ToPtr(zoneName)
|
||||
request.Host = xtypes.ToPtr(subDomain)
|
||||
request.Type = xtypes.ToPtr("TXT")
|
||||
|
||||
response, err := d.client.QueryRecordList(request)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if response.ReturnObj == nil || response.ReturnObj.Records == nil || len(response.ReturnObj.Records) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return response.ReturnObj.Records[0].RecordId, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
// 新增解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11259&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.AddRecordRequest{
|
||||
Domain: xtypes.ToPtr(zoneName),
|
||||
Host: xtypes.ToPtr(subDomain),
|
||||
Type: xtypes.ToPtr("TXT"),
|
||||
LineCode: xtypes.ToPtr("Default"),
|
||||
Value: xtypes.ToPtr(value),
|
||||
State: xtypes.ToPtr(int32(1)),
|
||||
TTL: xtypes.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.AddRecord(request)
|
||||
return err
|
||||
} else {
|
||||
// 修改解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11261&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.UpdateRecordRequest{
|
||||
RecordId: xtypes.ToPtr(recordId),
|
||||
Domain: xtypes.ToPtr(zoneName),
|
||||
Host: xtypes.ToPtr(subDomain),
|
||||
Type: xtypes.ToPtr("TXT"),
|
||||
LineCode: xtypes.ToPtr("Default"),
|
||||
Value: xtypes.ToPtr(value),
|
||||
State: xtypes.ToPtr(int32(1)),
|
||||
TTL: xtypes.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
recordId, err := d.findDNSRecordId(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if recordId == 0 {
|
||||
return nil
|
||||
} else {
|
||||
// 删除解析记录
|
||||
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=122&api=11262&data=181&isNormal=1&vid=259
|
||||
request := &ctyundns.DeleteRecordRequest{
|
||||
RecordId: xtypes.ToPtr(recordId),
|
||||
}
|
||||
_, err = d.client.DeleteRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package desec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/desec"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Token string `json:"token"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := desec.NewDefaultConfig()
|
||||
providerConfig.Token = config.Token
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := desec.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/digitalocean"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := digitalocean.NewDefaultConfig()
|
||||
providerConfig.AuthToken = config.AccessToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := digitalocean.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package dnsla
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/dnsla/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiId string `json:"apiId"`
|
||||
ApiSecret string `json:"apiSecret"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.APIId = config.ApiId
|
||||
providerConfig.APISecret = config.ApiSecret
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
|
||||
dnslasdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dnsla"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "DNSLA_"
|
||||
|
||||
EnvAPIId = envNamespace + "API_ID"
|
||||
EnvAPISecret = envNamespace + "API_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
APIId string
|
||||
APISecret string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *dnslasdk.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 300),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAPIId, EnvAPISecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIId = values[EnvAPIId]
|
||||
config.APISecret = values[EnvAPISecret]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("dnsla: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := dnslasdk.NewClient(config.APIId, config.APISecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("dnsla: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*dnslasdk.DomainRecord, error) {
|
||||
pageIndex := int32(1)
|
||||
pageSize := int32(100)
|
||||
for {
|
||||
request := &dnslasdk.ListDomainsRequest{
|
||||
PageIndex: &pageIndex,
|
||||
PageSize: &pageSize,
|
||||
}
|
||||
response, err := d.client.ListDomains(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, item := range response.Data.Results {
|
||||
if strings.TrimRight(item.Domain, ".") == zoneName || strings.TrimRight(item.DisplayDomain, ".") == zoneName {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < int(pageSize) {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("dnsla: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*dnslasdk.DomainRecord, *dnslasdk.DnsRecord, error) {
|
||||
zone, err := d.getDNSZone(zoneName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageIndex := int32(1)
|
||||
pageSize := int32(100)
|
||||
for {
|
||||
request := &dnslasdk.ListRecordsRequest{
|
||||
DomainId: &zone.Id,
|
||||
Host: &subDomain,
|
||||
PageIndex: &pageIndex,
|
||||
PageSize: &pageSize,
|
||||
}
|
||||
response, err := d.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return zone, nil, err
|
||||
}
|
||||
|
||||
if response.Data != nil {
|
||||
for _, record := range response.Data.Results {
|
||||
if record.Type == 16 && (record.Host == subDomain || record.DisplayHost == subDomain) {
|
||||
return zone, record, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if response.Data == nil || len(response.Data.Results) < int(pageSize) {
|
||||
break
|
||||
}
|
||||
|
||||
pageIndex++
|
||||
}
|
||||
|
||||
return zone, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &dnslasdk.CreateRecordRequest{
|
||||
DomainId: zone.Id,
|
||||
Type: 16,
|
||||
Host: subDomain,
|
||||
Data: value,
|
||||
Ttl: int32(d.config.TTL),
|
||||
}
|
||||
_, err := d.client.CreateRecord(request)
|
||||
return err
|
||||
} else {
|
||||
reqType := int32(16)
|
||||
reqTtl := int32(d.config.TTL)
|
||||
request := &dnslasdk.UpdateRecordRequest{
|
||||
Id: record.Id,
|
||||
Type: &reqType,
|
||||
Host: &subDomain,
|
||||
Data: &value,
|
||||
Ttl: &reqTtl,
|
||||
}
|
||||
_, err := d.client.UpdateRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
_, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
_, err = d.client.DeleteRecord(record.Id)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/duckdns"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Token string `json:"token"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := duckdns.NewDefaultConfig()
|
||||
providerConfig.Token = config.Token
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := duckdns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package dynv6
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/dynv6/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
HttpToken string `json:"httpToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.HTTPToken = config.HttpToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/libdns/dynv6"
|
||||
"github.com/libdns/libdns"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "DYNV6_"
|
||||
|
||||
EnvHTTPToken = envNamespace + "HTTP_TOKEN"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
HTTPToken string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *dynv6.Provider
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvHTTPToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.HTTPToken = values[EnvHTTPToken]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("dynv6: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client := &dynv6.Provider{Token: config.HTTPToken}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dynv6: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("dynv6: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*libdns.Record, error) {
|
||||
records, err := d.client.GetRecords(context.Background(), zoneName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
if record.Type == "TXT" && record.Name == subDomain {
|
||||
return &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
record = &libdns.Record{
|
||||
Type: "TXT",
|
||||
Name: subDomain,
|
||||
Value: value,
|
||||
TTL: time.Duration(d.config.TTL) * time.Second,
|
||||
}
|
||||
_, err := d.client.AppendRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||
return err
|
||||
} else {
|
||||
record.Value = value
|
||||
_, err := d.client.SetRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
_, err = d.client.DeleteRecords(context.Background(), zoneName, []libdns.Record{*record})
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package gcore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/gcore"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiToken string `json:"apiToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := gcore.NewDefaultConfig()
|
||||
providerConfig.APIToken = config.ApiToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := gcore.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package gname
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/gname/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AppId string `json:"appId"`
|
||||
AppKey string `json:"appKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AppID = config.AppId
|
||||
providerConfig.AppKey = config.AppKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
|
||||
gnamesdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/gname"
|
||||
xtypes "github.com/usual2970/certimate/internal/pkg/utils/types"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "GNAME_"
|
||||
|
||||
EnvAppID = envNamespace + "APP_ID"
|
||||
EnvAppKey = envNamespace + "APP_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AppID string
|
||||
AppKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *gnamesdk.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 300),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAppID, EnvAppKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AppID = values[EnvAppID]
|
||||
config.AppKey = values[EnvAppKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("gname: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := gnamesdk.NewClient(config.AppID, config.AppKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
client.SetTimeout(config.HTTPTimeout)
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("gname: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(zoneName, subDomain string) (*gnamesdk.DomainResolutionRecordord, error) {
|
||||
page := int32(1)
|
||||
pageSize := int32(20)
|
||||
for {
|
||||
request := &gnamesdk.ListDomainResolutionRequest{
|
||||
ZoneName: xtypes.ToPtr(zoneName),
|
||||
Page: xtypes.ToPtr(page),
|
||||
PageSize: xtypes.ToPtr(pageSize),
|
||||
}
|
||||
|
||||
response, err := d.client.ListDomainResolution(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Data {
|
||||
if record.RecordType == "TXT" && record.RecordName == subDomain {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Data) == 0 {
|
||||
break
|
||||
}
|
||||
if response.Page*response.PageSize >= response.Count {
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := &gnamesdk.AddDomainResolutionRequest{
|
||||
ZoneName: xtypes.ToPtr(zoneName),
|
||||
RecordType: xtypes.ToPtr("TXT"),
|
||||
RecordName: xtypes.ToPtr(subDomain),
|
||||
RecordValue: xtypes.ToPtr(value),
|
||||
TTL: xtypes.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.AddDomainResolution(request)
|
||||
return err
|
||||
} else {
|
||||
recordId, _ := record.ID.Int64()
|
||||
request := &gnamesdk.ModifyDomainResolutionRequest{
|
||||
ID: xtypes.ToPtr(recordId),
|
||||
ZoneName: xtypes.ToPtr(zoneName),
|
||||
RecordType: xtypes.ToPtr("TXT"),
|
||||
RecordName: xtypes.ToPtr(subDomain),
|
||||
RecordValue: xtypes.ToPtr(value),
|
||||
TTL: xtypes.ToPtr(int32(d.config.TTL)),
|
||||
}
|
||||
_, err := d.client.ModifyDomainResolution(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
record, err := d.findDNSRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
recordId, _ := record.ID.Int64()
|
||||
request := &gnamesdk.DeleteDomainResolutionRequest{
|
||||
ZoneName: xtypes.ToPtr(zoneName),
|
||||
RecordID: xtypes.ToPtr(recordId),
|
||||
}
|
||||
_, err = d.client.DeleteDomainResolution(request)
|
||||
return err
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package godaddy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/godaddy"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
ApiSecret string `json:"apiSecret"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := godaddy.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
providerConfig.APISecret = config.ApiSecret
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := godaddy.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/hetzner"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiToken string `json:"apiToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := hetzner.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := hetzner.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package huaweicloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
hwc "github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
region := config.Region
|
||||
if region == "" {
|
||||
// 华为云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
|
||||
region = "cn-north-1"
|
||||
}
|
||||
|
||||
providerConfig := hwc.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.SecretAccessKey = config.SecretAccessKey
|
||||
providerConfig.Region = region
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := hwc.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
|
||||
jddnsapi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
|
||||
jddnsclient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
|
||||
jddnsmodel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "JDCLOUD_"
|
||||
|
||||
EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
|
||||
EnvAccessKeySecret = envNamespace + "ACCESS_KEY_SECRET"
|
||||
EnvRegionId = envNamespace + "REGION_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
RegionId string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *jddnsclient.DomainserviceClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKeyID, EnvAccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKeyID = values[EnvAccessKeyID]
|
||||
config.AccessKeySecret = values[EnvAccessKeySecret]
|
||||
config.RegionId = values[EnvRegionId]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
clientCredentials := jdcore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
|
||||
client := jddnsclient.NewDomainserviceClient(clientCredentials)
|
||||
clientConfig := &client.Config
|
||||
clientConfig.SetTimeout(config.HTTPTimeout)
|
||||
client.SetConfig(clientConfig)
|
||||
client.SetLogger(jdcore.NewDefaultLogger(jdcore.LogWarn))
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(authZone), subDomain, info.Value); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(authZone), subDomain); err != nil {
|
||||
return fmt.Errorf("jdcloud: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZone(zoneName string) (*jddnsmodel.DomainInfo, error) {
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
for {
|
||||
request := jddnsapi.NewDescribeDomainsRequest(d.config.RegionId, pageNumber, pageSize)
|
||||
request.SetDomainName(zoneName)
|
||||
|
||||
response, err := d.client.DescribeDomains(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range response.Result.DataList {
|
||||
if item.DomainName == zoneName {
|
||||
return &item, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Result.DataList) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("jdcloud: zone %s not found", zoneName)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getDNSZoneAndRecord(zoneName, subDomain string) (*jddnsmodel.DomainInfo, *jddnsmodel.RRInfo, error) {
|
||||
zone, err := d.getDNSZone(zoneName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pageNumber := 1
|
||||
pageSize := 10
|
||||
for {
|
||||
request := jddnsapi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id))
|
||||
request.SetSearch(subDomain)
|
||||
request.SetPageNumber(pageNumber)
|
||||
request.SetPageSize(pageSize)
|
||||
|
||||
response, err := d.client.DescribeResourceRecord(request)
|
||||
if err != nil {
|
||||
return zone, nil, err
|
||||
}
|
||||
|
||||
for _, record := range response.Result.DataList {
|
||||
if record.Type == "TXT" && record.HostRecord == subDomain {
|
||||
return zone, &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Result.DataList) < pageSize {
|
||||
break
|
||||
}
|
||||
|
||||
pageNumber++
|
||||
}
|
||||
|
||||
return zone, nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(zoneName, subDomain, value string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := jddnsapi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), &jddnsmodel.AddRR{
|
||||
Type: "TXT",
|
||||
HostRecord: subDomain,
|
||||
HostValue: value,
|
||||
Ttl: int(d.config.TTL),
|
||||
ViewValue: -1,
|
||||
})
|
||||
_, err := d.client.CreateResourceRecord(request)
|
||||
return err
|
||||
} else {
|
||||
request := jddnsapi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id), &jddnsmodel.UpdateRR{
|
||||
Type: "TXT",
|
||||
HostRecord: subDomain,
|
||||
HostValue: value,
|
||||
Ttl: int(d.config.TTL),
|
||||
ViewValue: -1,
|
||||
})
|
||||
_, err := d.client.ModifyResourceRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(zoneName, subDomain string) error {
|
||||
zone, record, err := d.getDNSZoneAndRecord(zoneName, subDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := jddnsapi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", zone.Id), fmt.Sprintf("%d", record.Id))
|
||||
_, err = d.client.DeleteResourceRecord(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package jdcloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/jdcloud/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
RegionId string `json:"regionId"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
regionId := config.RegionId
|
||||
if regionId == "" {
|
||||
// 京东云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
|
||||
regionId = "cn-north-1"
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.AccessKeyID = config.AccessKeyId
|
||||
providerConfig.AccessKeySecret = config.AccessKeySecret
|
||||
providerConfig.RegionId = regionId
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/namecheap"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Username string `json:"username"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := namecheap.NewDefaultConfig()
|
||||
providerConfig.APIUser = config.Username
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := namecheap.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Username string `json:"username"`
|
||||
ApiToken string `json:"apiToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := namedotcom.NewDefaultConfig()
|
||||
providerConfig.Username = config.Username
|
||||
providerConfig.APIToken = config.ApiToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := namedotcom.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package namesilo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/namesilo"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := namesilo.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := namesilo.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package netcup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/netcup"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
CustomerNumber string `json:"customerNumber"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
ApiPassword string `json:"apiPassword"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := netcup.NewDefaultConfig()
|
||||
providerConfig.Customer = config.CustomerNumber
|
||||
providerConfig.Key = config.ApiKey
|
||||
providerConfig.Password = config.ApiPassword
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := netcup.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package netcup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/netlify"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiToken string `json:"apiToken"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := netlify.NewDefaultConfig()
|
||||
providerConfig.Token = config.ApiToken
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := netlify.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package ns1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/ns1"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := ns1.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := ns1.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package porkbun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/porkbun"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
SecretApiKey string `json:"secretApiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := porkbun.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
providerConfig.SecretAPIKey = config.SecretApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := porkbun.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package powerdns
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/pdns"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
serverUrl, _ := url.Parse(config.ServerUrl)
|
||||
providerConfig := pdns.NewDefaultConfig()
|
||||
providerConfig.Host = serverUrl
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.AllowInsecureConnections {
|
||||
providerConfig.HTTPClient.Transport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := pdns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package rainyun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/rainyun"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := rainyun.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := rainyun.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
teo "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo/v20220901"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "TENCENTCLOUDEO_"
|
||||
|
||||
EnvSecretID = envNamespace + "SECRET_ID"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
EnvZoneID = envNamespace + "ZONE_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
SecretID string
|
||||
SecretKey string
|
||||
ZoneID string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *teo.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvSecretID, EnvSecretKey, EnvZoneID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tencentcloud-eo: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.SecretID = values[EnvSecretID]
|
||||
config.SecretKey = values[EnvSecretKey]
|
||||
config.ZoneID = values[EnvSecretKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("tencentcloud-eo: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
credential := common.NewCredential(config.SecretID, config.SecretKey)
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds()))
|
||||
client, err := teo.NewClient(credential, "", cpf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
if err := d.addOrUpdateDNSRecord(dns01.UnFqdn(info.EffectiveFQDN), info.Value); err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
if err := d.removeDNSRecord(dns01.UnFqdn(info.EffectiveFQDN)); err != nil {
|
||||
return fmt.Errorf("tencentcloud-eo: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) findDNSRecord(effectiveFQDN string) (*teo.DnsRecord, error) {
|
||||
pageOffset := 0
|
||||
pageLimit := 1000
|
||||
for {
|
||||
request := teo.NewDescribeDnsRecordsRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.Offset = common.Int64Ptr(int64(pageOffset))
|
||||
request.Limit = common.Int64Ptr(int64(pageLimit))
|
||||
request.Filters = []*teo.AdvancedFilter{
|
||||
{
|
||||
Name: common.StringPtr("type"),
|
||||
Values: []*string{common.StringPtr("TXT")},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := d.client.DescribeDnsRecords(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Response == nil {
|
||||
break
|
||||
} else {
|
||||
for _, record := range response.Response.DnsRecords {
|
||||
if *record.Name == effectiveFQDN {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(response.Response.DnsRecords) < pageLimit {
|
||||
break
|
||||
}
|
||||
|
||||
pageOffset += len(response.Response.DnsRecords)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) addOrUpdateDNSRecord(effectiveFQDN, value string) error {
|
||||
record, err := d.findDNSRecord(effectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
request := teo.NewCreateDnsRecordRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.Name = common.StringPtr(effectiveFQDN)
|
||||
request.Type = common.StringPtr("TXT")
|
||||
request.Content = common.StringPtr(value)
|
||||
request.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||
_, err := d.client.CreateDnsRecord(request)
|
||||
return err
|
||||
} else {
|
||||
record.Content = common.StringPtr(value)
|
||||
record.TTL = common.Int64Ptr(int64(d.config.TTL))
|
||||
request := teo.NewModifyDnsRecordsRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.DnsRecords = []*teo.DnsRecord{record}
|
||||
if _, err := d.client.ModifyDnsRecords(request); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *record.Status == "disable" {
|
||||
request := teo.NewModifyDnsRecordsStatusRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.RecordsToEnable = []*string{record.RecordId}
|
||||
if _, err = d.client.ModifyDnsRecordsStatus(request); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) removeDNSRecord(effectiveFQDN string) error {
|
||||
record, err := d.findDNSRecord(effectiveFQDN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return nil
|
||||
} else {
|
||||
request := teo.NewDeleteDnsRecordsRequest()
|
||||
request.ZoneId = common.StringPtr(d.config.ZoneID)
|
||||
request.RecordIds = []*string{record.RecordId}
|
||||
_, err = d.client.DeleteDnsRecords(request)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package tencentcloudeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/tencentcloud-eo/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
SecretId string `json:"secretId"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
ZoneId string `json:"zoneId"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.SecretID = config.SecretId
|
||||
providerConfig.SecretKey = config.SecretKey
|
||||
providerConfig.ZoneID = config.ZoneId
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package tencentcloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
SecretId string `json:"secretId"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := tencentcloud.NewDefaultConfig()
|
||||
providerConfig.SecretID = config.SecretId
|
||||
providerConfig.SecretKey = config.SecretKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := tencentcloud.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud"
|
||||
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/sdk3rd/ucloud/udnr"
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "UCLOUDUDNR_"
|
||||
|
||||
EnvPublicKey = envNamespace + "PUBLIC_KEY"
|
||||
EnvPrivateKey = envNamespace + "PRIVATE_KEY"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
|
||||
|
||||
type Config struct {
|
||||
PrivateKey string
|
||||
PublicKey string
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int32
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
client *udnr.UDNRClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvPrivateKey, EnvPublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ucloud-udnr: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.PrivateKey = values[EnvPrivateKey]
|
||||
config.PublicKey = values[EnvPublicKey]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("ucloud-udnr: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
cfg := ucloud.NewConfig()
|
||||
cfg.Timeout = config.HTTPTimeout
|
||||
credential := auth.NewCredential()
|
||||
credential.PrivateKey = config.PrivateKey
|
||||
credential.PublicKey = config.PublicKey
|
||||
client := udnr.NewClient(&cfg, &credential)
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
}
|
||||
|
||||
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
} else {
|
||||
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||
if record.DnsType == "TXT" && record.RecordName == subDomain {
|
||||
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
udnrDomainDNSAddReq := d.client.NewAddDomainDNSRequest()
|
||||
udnrDomainDNSAddReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSAddReq.DnsType = ucloud.String("TXT")
|
||||
udnrDomainDNSAddReq.RecordName = ucloud.String(subDomain)
|
||||
udnrDomainDNSAddReq.Content = ucloud.String(info.Value)
|
||||
udnrDomainDNSAddReq.TTL = ucloud.Int(int(d.config.TTL))
|
||||
if _, err := d.client.AddDomainDNS(udnrDomainDNSAddReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: could not find zone for domain %q: %w", domain, err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
}
|
||||
|
||||
udnrDomainDNSQueryReq := d.client.NewQueryDomainDNSRequest()
|
||||
udnrDomainDNSQueryReq.Dn = ucloud.String(authZone)
|
||||
if udnrDomainDNSQueryResp, err := d.client.QueryDomainDNS(udnrDomainDNSQueryReq); err != nil {
|
||||
return fmt.Errorf("ucloud-udnr: %w", err)
|
||||
} else {
|
||||
for _, record := range udnrDomainDNSQueryResp.Data {
|
||||
if record.DnsType == "TXT" && record.RecordName == subDomain {
|
||||
udnrDomainDNSDeleteReq := d.client.NewDeleteDomainDNSRequest()
|
||||
udnrDomainDNSDeleteReq.Dn = ucloud.String(authZone)
|
||||
udnrDomainDNSDeleteReq.DnsType = ucloud.String(record.DnsType)
|
||||
udnrDomainDNSDeleteReq.RecordName = ucloud.String(record.RecordName)
|
||||
udnrDomainDNSDeleteReq.Content = ucloud.String(record.Content)
|
||||
d.client.DeleteDomainDNS(udnrDomainDNSDeleteReq)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package ucloududnr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
"github.com/usual2970/certimate/internal/pkg/core/ssl-applicator/acme-dns01/providers/ucloud-udnr/internal"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is nil")
|
||||
}
|
||||
|
||||
providerConfig := internal.NewDefaultConfig()
|
||||
providerConfig.PrivateKey = config.PrivateKey
|
||||
providerConfig.PublicKey = config.PublicKey
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = config.DnsTTL
|
||||
}
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := internal.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package vercel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/vercel"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiAccessToken string `json:"apiAccessToken"`
|
||||
TeamId string `json:"teamId,omitempty"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := vercel.NewDefaultConfig()
|
||||
providerConfig.AuthToken = config.ApiAccessToken
|
||||
providerConfig.TeamID = config.TeamId
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := vercel.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package volcengine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/volcengine"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := volcengine.NewDefaultConfig()
|
||||
providerConfig.AccessKey = config.AccessKeyId
|
||||
providerConfig.SecretKey = config.SecretAccessKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := volcengine.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package westcn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/westcn"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
Username string `json:"username"`
|
||||
ApiPassword string `json:"apiPassword"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := westcn.NewDefaultConfig()
|
||||
providerConfig.Username = config.Username
|
||||
providerConfig.Password = config.ApiPassword
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := westcn.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package onepanelconsole
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||
onepanelsdkv2 "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel/v2"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// 1Panel 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// 1Panel 版本。
|
||||
// 可取值 "v1"、"v2"。
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
// 1Panel 接口密钥。
|
||||
ApiKey string `json:"apiKey"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
// 是否自动重启。
|
||||
AutoRestart bool `json:"autoRestart"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient any
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
|
||||
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
// 设置面板 SSL 证书
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
{
|
||||
updateSettingsSSLReq := &onepanelsdk.UpdateSettingsSSLRequest{
|
||||
Cert: certPEM,
|
||||
Key: privkeyPEM,
|
||||
SSL: "enable",
|
||||
SSLType: "import-paste",
|
||||
AutoRestart: strconv.FormatBool(d.config.AutoRestart),
|
||||
}
|
||||
updateSystemSSLResp, err := sdkClient.UpdateSettingsSSL(updateSettingsSSLReq)
|
||||
d.logger.Debug("sdk request '1panel.UpdateSettingsSSL'", slog.Any("request", updateSettingsSSLReq), slog.Any("response", updateSystemSSLResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.UpdateSettingsSSL': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
case *onepanelsdkv2.Client:
|
||||
{
|
||||
updateCoreSettingsSSLReq := &onepanelsdkv2.UpdateCoreSettingsSSLRequest{
|
||||
Cert: certPEM,
|
||||
Key: privkeyPEM,
|
||||
SSL: "Enable",
|
||||
SSLType: "import-paste",
|
||||
AutoRestart: strconv.FormatBool(d.config.AutoRestart),
|
||||
}
|
||||
updateCoreSystemSSLResp, err := sdkClient.UpdateCoreSettingsSSL(updateCoreSettingsSSLReq)
|
||||
d.logger.Debug("sdk request '1panel.UpdateCoreSettingsSSL'", slog.Any("request", updateCoreSettingsSSLReq), slog.Any("response", updateCoreSystemSSLResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.UpdateCoreSettingsSSL': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("sdk client is not implemented")
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
sdkVersionV1 = "v1"
|
||||
sdkVersionV2 = "v2"
|
||||
)
|
||||
|
||||
func createSDKClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (any, error) {
|
||||
if apiVersion == sdkVersionV1 {
|
||||
client, err := onepanelsdk.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
} else if apiVersion == sdkVersionV2 {
|
||||
client, err := onepanelsdkv2.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid 1panel api version")
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package onepanelconsole_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/1panel-console"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fServerUrl string
|
||||
fApiVersion string
|
||||
fApiKey string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
|
||||
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
|
||||
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./1panel_console_test.go -args \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_SERVERURL="http://127.0.0.1:20410" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_APIVERSION="v1" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELCONSOLE_APIKEY="your-api-key"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("SERVERURL: %v", fServerUrl),
|
||||
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
ApiVersion: fApiVersion,
|
||||
ApiKey: fApiKey,
|
||||
AllowInsecureConnections: true,
|
||||
AutoRestart: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
package onepanelsite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
sslmgrsp "github.com/usual2970/certimate/internal/pkg/core/ssl-manager/providers/1panel-ssl"
|
||||
onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel"
|
||||
onepanelsdkv2 "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel/v2"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// 1Panel 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// 1Panel 版本。
|
||||
// 可取值 "v1"、"v2"。
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
// 1Panel 接口密钥。
|
||||
ApiKey string `json:"apiKey"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
// 部署资源类型。
|
||||
ResourceType ResourceType `json:"resourceType"`
|
||||
// 网站 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_WEBSITE] 时必填。
|
||||
WebsiteId int64 `json:"websiteId,omitempty"`
|
||||
// 证书 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||
CertificateId int64 `json:"certificateId,omitempty"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient any
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
|
||||
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
ServerUrl: config.ServerUrl,
|
||||
ApiVersion: config.ApiVersion,
|
||||
ApiKey: config.ApiKey,
|
||||
AllowInsecureConnections: config.AllowInsecureConnections,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
|
||||
d.sslManager.SetLogger(logger)
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
// 根据部署资源类型决定部署方式
|
||||
switch d.config.ResourceType {
|
||||
case RESOURCE_TYPE_WEBSITE:
|
||||
if err := d.deployToWebsite(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case RESOURCE_TYPE_CERTIFICATE:
|
||||
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToWebsite(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.WebsiteId == 0 {
|
||||
return errors.New("config `websiteId` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
getHttpsConfResp, err := sdkClient.GetHttpsConf(d.config.WebsiteId)
|
||||
d.logger.Debug("sdk request '1panel.GetHttpsConf'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("response", getHttpsConfResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.GetHttpsConf': %w", err)
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: certId,
|
||||
Enable: getHttpsConfResp.Data.Enable,
|
||||
HttpConfig: getHttpsConfResp.Data.HttpConfig,
|
||||
SSLProtocol: getHttpsConfResp.Data.SSLProtocol,
|
||||
Algorithm: getHttpsConfResp.Data.Algorithm,
|
||||
Hsts: getHttpsConfResp.Data.Hsts,
|
||||
}
|
||||
updateHttpsConfResp, err := sdkClient.UpdateHttpsConf(d.config.WebsiteId, updateHttpsConfReq)
|
||||
d.logger.Debug("sdk request '1panel.UpdateHttpsConf'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("request", updateHttpsConfReq), slog.Any("response", updateHttpsConfResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.UpdateHttpsConf': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
case *onepanelsdkv2.Client:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
getHttpsConfResp, err := sdkClient.GetHttpsConf(d.config.WebsiteId)
|
||||
d.logger.Debug("sdk request '1panel.GetHttpsConf'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("response", getHttpsConfResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.GetHttpsConf': %w", err)
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
updateHttpsConfReq := &onepanelsdkv2.UpdateHttpsConfRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: certId,
|
||||
Enable: getHttpsConfResp.Data.Enable,
|
||||
HttpConfig: getHttpsConfResp.Data.HttpConfig,
|
||||
SSLProtocol: getHttpsConfResp.Data.SSLProtocol,
|
||||
Algorithm: getHttpsConfResp.Data.Algorithm,
|
||||
Hsts: getHttpsConfResp.Data.Hsts,
|
||||
}
|
||||
updateHttpsConfResp, err := sdkClient.UpdateHttpsConf(d.config.WebsiteId, updateHttpsConfReq)
|
||||
d.logger.Debug("sdk request '1panel.UpdateHttpsConf'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("request", updateHttpsConfReq), slog.Any("response", updateHttpsConfResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.UpdateHttpsConf': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("sdk client is not implemented")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.CertificateId == 0 {
|
||||
return errors.New("config `certificateId` is required")
|
||||
}
|
||||
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
{
|
||||
// 获取证书详情
|
||||
getWebsiteSSLResp, err := sdkClient.GetWebsiteSSL(d.config.CertificateId)
|
||||
d.logger.Debug("sdk request '1panel.GetWebsiteSSL'", slog.Any("sslId", d.config.CertificateId), slog.Any("response", getWebsiteSSLResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.GetWebsiteSSL': %w", err)
|
||||
}
|
||||
|
||||
// 更新证书
|
||||
uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
|
||||
SSLID: d.config.CertificateId,
|
||||
Type: "paste",
|
||||
Description: getWebsiteSSLResp.Data.Description,
|
||||
Certificate: certPEM,
|
||||
PrivateKey: privkeyPEM,
|
||||
}
|
||||
uploadWebsiteSSLResp, err := sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq)
|
||||
d.logger.Debug("sdk request '1panel.UploadWebsiteSSL'", slog.Any("request", uploadWebsiteSSLReq), slog.Any("response", uploadWebsiteSSLResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.UploadWebsiteSSL': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
case *onepanelsdkv2.Client:
|
||||
{
|
||||
// 获取证书详情
|
||||
getWebsiteSSLResp, err := sdkClient.GetWebsiteSSL(d.config.CertificateId)
|
||||
d.logger.Debug("sdk request '1panel.GetWebsiteSSL'", slog.Any("sslId", d.config.CertificateId), slog.Any("response", getWebsiteSSLResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.GetWebsiteSSL': %w", err)
|
||||
}
|
||||
|
||||
// 更新证书
|
||||
uploadWebsiteSSLReq := &onepanelsdkv2.UploadWebsiteSSLRequest{
|
||||
SSLID: d.config.CertificateId,
|
||||
Type: "paste",
|
||||
Description: getWebsiteSSLResp.Data.Description,
|
||||
Certificate: certPEM,
|
||||
PrivateKey: privkeyPEM,
|
||||
}
|
||||
uploadWebsiteSSLResp, err := sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq)
|
||||
d.logger.Debug("sdk request '1panel.UploadWebsiteSSL'", slog.Any("request", uploadWebsiteSSLReq), slog.Any("response", uploadWebsiteSSLResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.UploadWebsiteSSL': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("sdk client is not implemented")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
sdkVersionV1 = "v1"
|
||||
sdkVersionV2 = "v2"
|
||||
)
|
||||
|
||||
func createSDKClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (any, error) {
|
||||
if apiVersion == sdkVersionV1 {
|
||||
client, err := onepanelsdk.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
} else if apiVersion == sdkVersionV2 {
|
||||
client, err := onepanelsdkv2.NewClient(serverUrl, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid 1panel api version")
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package onepanelsite_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/1panel-site"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fServerUrl string
|
||||
fApiVersion string
|
||||
fApiKey string
|
||||
fWebsiteId int64
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_SSLDEPLOYER_1PANELSITE_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
|
||||
flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "")
|
||||
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
|
||||
flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./1panel_site_test.go -args \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_SERVERURL="http://127.0.0.1:20410" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_APIVERSION="v1" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_APIKEY="your-api-key" \
|
||||
--CERTIMATE_SSLDEPLOYER_1PANELSITE_WEBSITEID="your-website-id"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("SERVERURL: %v", fServerUrl),
|
||||
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||
fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
ApiVersion: fApiVersion,
|
||||
ApiKey: fApiKey,
|
||||
AllowInsecureConnections: true,
|
||||
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
|
||||
WebsiteId: fWebsiteId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package onepanelsite
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
// 资源类型:替换指定网站的证书。
|
||||
RESOURCE_TYPE_WEBSITE = ResourceType("website")
|
||||
// 资源类型:替换指定证书。
|
||||
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||
)
|
||||
@@ -1,487 +0,0 @@
|
||||
package aliyunalb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
alialb "github.com/alibabacloud-go/alb-20200616/v2/client"
|
||||
alicas "github.com/alibabacloud-go/cas-20200407/v3/client"
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
sslmgrsp "github.com/usual2970/certimate/internal/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// 阿里云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 阿里云资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 部署资源类型。
|
||||
ResourceType ResourceType `json:"resourceType"`
|
||||
// 负载均衡实例 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。
|
||||
LoadbalancerId string `json:"loadbalancerId,omitempty"`
|
||||
// 负载均衡监听 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
|
||||
ListenerId string `json:"listenerId,omitempty"`
|
||||
// SNI 域名(支持泛域名)。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。
|
||||
Domain string `json:"domain,omitempty"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClients *wSDKClients
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
|
||||
type wSDKClients struct {
|
||||
ALB *alialb.Client
|
||||
CAS *alicas.Client
|
||||
}
|
||||
|
||||
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||
}
|
||||
|
||||
clients, err := createSDKClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := createSSLManager(config.AccessKeyId, config.AccessKeySecret, config.ResourceGroupId, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClients: clients,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
|
||||
d.sslManager.SetLogger(logger)
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.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_LOADBALANCER:
|
||||
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case RESOURCE_TYPE_LISTENER:
|
||||
if err := d.deployToListener(ctx, upres.CertId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
|
||||
if d.config.LoadbalancerId == "" {
|
||||
return errors.New("config `loadbalancerId` is required")
|
||||
}
|
||||
|
||||
// 查询负载均衡实例的详细信息
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getloadbalancerattribute
|
||||
getLoadBalancerAttributeReq := &alialb.GetLoadBalancerAttributeRequest{
|
||||
LoadBalancerId: tea.String(d.config.LoadbalancerId),
|
||||
}
|
||||
getLoadBalancerAttributeResp, err := d.sdkClients.ALB.GetLoadBalancerAttribute(getLoadBalancerAttributeReq)
|
||||
d.logger.Debug("sdk request 'alb.GetLoadBalancerAttribute'", slog.Any("request", getLoadBalancerAttributeReq), slog.Any("response", getLoadBalancerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetLoadBalancerAttribute': %w", err)
|
||||
}
|
||||
|
||||
// 查询 HTTPS 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||
listenerIds := make([]string, 0)
|
||||
listListenersLimit := int32(100)
|
||||
var listListenersToken *string = nil
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listListenersReq := &alialb.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
ListenerProtocol: tea.String("HTTPS"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
|
||||
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) == 0 || listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
}
|
||||
|
||||
// 查询 QUIC 监听列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlisteners
|
||||
listListenersToken = nil
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listListenersReq := &alialb.ListListenersRequest{
|
||||
MaxResults: tea.Int32(listListenersLimit),
|
||||
NextToken: listListenersToken,
|
||||
LoadBalancerIds: []*string{tea.String(d.config.LoadbalancerId)},
|
||||
ListenerProtocol: tea.String("QUIC"),
|
||||
}
|
||||
listListenersResp, err := d.sdkClients.ALB.ListListeners(listListenersReq)
|
||||
d.logger.Debug("sdk request 'alb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListeners': %w", err)
|
||||
}
|
||||
|
||||
if listListenersResp.Body.Listeners != nil {
|
||||
for _, listener := range listListenersResp.Body.Listeners {
|
||||
listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId))
|
||||
}
|
||||
}
|
||||
|
||||
if len(listListenersResp.Body.Listeners) == 0 || listListenersResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenersToken = listListenersResp.Body.NextToken
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历更新监听证书
|
||||
if len(listenerIds) == 0 {
|
||||
d.logger.Info("no alb listeners to deploy")
|
||||
} else {
|
||||
var errs []error
|
||||
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||
|
||||
for _, listenerId := range listenerIds {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
|
||||
if d.config.ListenerId == "" {
|
||||
return errors.New("config `listenerId` is required")
|
||||
}
|
||||
|
||||
// 更新监听
|
||||
if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
|
||||
// 查询监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-getlistenerattribute
|
||||
getListenerAttributeReq := &alialb.GetListenerAttributeRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
}
|
||||
getListenerAttributeResp, err := d.sdkClients.ALB.GetListenerAttribute(getListenerAttributeReq)
|
||||
d.logger.Debug("sdk request 'alb.GetListenerAttribute'", slog.Any("request", getListenerAttributeReq), slog.Any("response", getListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.GetListenerAttribute': %w", err)
|
||||
}
|
||||
|
||||
if d.config.Domain == "" {
|
||||
// 未指定 SNI,只需部署到监听器
|
||||
|
||||
// 修改监听的属性
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-updatelistenerattribute
|
||||
updateListenerAttributeReq := &alialb.UpdateListenerAttributeRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
Certificates: []*alialb.UpdateListenerAttributeRequestCertificates{{
|
||||
CertificateId: tea.String(cloudCertId),
|
||||
}},
|
||||
}
|
||||
updateListenerAttributeResp, err := d.sdkClients.ALB.UpdateListenerAttribute(updateListenerAttributeReq)
|
||||
d.logger.Debug("sdk request 'alb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerAttributeResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.UpdateListenerAttribute': %w", err)
|
||||
}
|
||||
} else {
|
||||
// 指定 SNI,需部署到扩展域名
|
||||
|
||||
// 查询监听证书列表
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||
listenerCertificates := make([]alialb.ListListenerCertificatesResponseBodyCertificates, 0)
|
||||
listListenerCertificatesLimit := int32(100)
|
||||
var listListenerCertificatesToken *string = nil
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listListenerCertificatesReq := &alialb.ListListenerCertificatesRequest{
|
||||
NextToken: listListenerCertificatesToken,
|
||||
MaxResults: tea.Int32(listListenerCertificatesLimit),
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
CertificateType: tea.String("Server"),
|
||||
}
|
||||
listListenerCertificatesResp, err := d.sdkClients.ALB.ListListenerCertificates(listListenerCertificatesReq)
|
||||
d.logger.Debug("sdk request 'alb.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.ListListenerCertificates': %w", err)
|
||||
}
|
||||
|
||||
if listListenerCertificatesResp.Body.Certificates != nil {
|
||||
for _, listenerCertificate := range listListenerCertificatesResp.Body.Certificates {
|
||||
listenerCertificates = append(listenerCertificates, *listenerCertificate)
|
||||
}
|
||||
}
|
||||
|
||||
if len(listListenerCertificatesResp.Body.Certificates) == 0 || listListenerCertificatesResp.Body.NextToken == nil {
|
||||
break
|
||||
} else {
|
||||
listListenerCertificatesToken = listListenerCertificatesResp.Body.NextToken
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历查询监听证书,并找出需要解除关联的证书
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates
|
||||
// REF: https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-getusercertificatedetail
|
||||
certificateIsAlreadyAssociated := false
|
||||
certificateIdsToDissociate := make([]string, 0)
|
||||
if len(listenerCertificates) > 0 {
|
||||
d.logger.Info("found listener certificates to deploy", slog.Any("listenerCertificates", listenerCertificates))
|
||||
var errs []error
|
||||
|
||||
for _, listenerCertificate := range listenerCertificates {
|
||||
if tea.BoolValue(listenerCertificate.IsDefault) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.EqualFold(tea.StringValue(listenerCertificate.Status), "Associated") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 监听证书 ID 格式:${证书 ID}-${地域}
|
||||
certificateId := strings.Split(tea.StringValue(listenerCertificate.CertificateId), "-")[0]
|
||||
if certificateId == cloudCertId {
|
||||
certificateIsAlreadyAssociated = true
|
||||
break
|
||||
}
|
||||
|
||||
certificateIdAsInt64, err := strconv.ParseInt(certificateId, 10, 64)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
getUserCertificateDetailReq := &alicas.GetUserCertificateDetailRequest{
|
||||
CertId: tea.Int64(certificateIdAsInt64),
|
||||
}
|
||||
getUserCertificateDetailResp, err := d.sdkClients.CAS.GetUserCertificateDetail(getUserCertificateDetailReq)
|
||||
d.logger.Debug("sdk request 'cas.GetUserCertificateDetail'", slog.Any("request", getUserCertificateDetailReq), slog.Any("response", getUserCertificateDetailResp))
|
||||
if err != nil {
|
||||
if sdkerr, ok := err.(*tea.SDKError); ok {
|
||||
if tea.IntValue(sdkerr.StatusCode) == 400 && tea.StringValue(sdkerr.Code) == "NotFound" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
errs = append(errs, fmt.Errorf("failed to execute sdk request 'cas.GetUserCertificateDetail': %w", err))
|
||||
continue
|
||||
} else {
|
||||
certCNMatched := tea.StringValue(getUserCertificateDetailResp.Body.Common) == d.config.Domain
|
||||
certSANMatched := slices.Contains(strings.Split(tea.StringValue(getUserCertificateDetailResp.Body.Sans), ","), d.config.Domain)
|
||||
if !certCNMatched && !certSANMatched {
|
||||
continue
|
||||
}
|
||||
|
||||
certEndDate, _ := time.Parse("2006-01-02", tea.StringValue(getUserCertificateDetailResp.Body.EndDate))
|
||||
if time.Now().Before(certEndDate) {
|
||||
continue
|
||||
}
|
||||
|
||||
certificateIdsToDissociate = append(certificateIdsToDissociate, certificateId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
// 关联监听和扩展证书
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-associateadditionalcertificateswithlistener
|
||||
if !certificateIsAlreadyAssociated {
|
||||
associateAdditionalCertificatesFromListenerReq := &alialb.AssociateAdditionalCertificatesWithListenerRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
Certificates: []*alialb.AssociateAdditionalCertificatesWithListenerRequestCertificates{
|
||||
{
|
||||
CertificateId: tea.String(cloudCertId),
|
||||
},
|
||||
},
|
||||
}
|
||||
associateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesFromListenerReq)
|
||||
d.logger.Debug("sdk request 'alb.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesFromListenerReq), slog.Any("response", associateAdditionalCertificatesFromListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.AssociateAdditionalCertificatesWithListener': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 解除关联监听和扩展证书
|
||||
// REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-dissociateadditionalcertificatesfromlistener
|
||||
if !certificateIsAlreadyAssociated && len(certificateIdsToDissociate) > 0 {
|
||||
dissociateAdditionalCertificates := make([]*alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates, 0)
|
||||
for _, certificateId := range certificateIdsToDissociate {
|
||||
dissociateAdditionalCertificates = append(dissociateAdditionalCertificates, &alialb.DissociateAdditionalCertificatesFromListenerRequestCertificates{
|
||||
CertificateId: tea.String(certificateId),
|
||||
})
|
||||
}
|
||||
|
||||
dissociateAdditionalCertificatesFromListenerReq := &alialb.DissociateAdditionalCertificatesFromListenerRequest{
|
||||
ListenerId: tea.String(cloudListenerId),
|
||||
Certificates: dissociateAdditionalCertificates,
|
||||
}
|
||||
dissociateAdditionalCertificatesFromListenerResp, err := d.sdkClients.ALB.DissociateAdditionalCertificatesFromListener(dissociateAdditionalCertificatesFromListenerReq)
|
||||
d.logger.Debug("sdk request 'alb.DissociateAdditionalCertificatesFromListener'", slog.Any("request", dissociateAdditionalCertificatesFromListenerReq), slog.Any("response", dissociateAdditionalCertificatesFromListenerResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'alb.DissociateAdditionalCertificatesFromListener': %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClients(accessKeyId, accessKeySecret, region string) (*wSDKClients, error) {
|
||||
// 接入点一览 https://api.aliyun.com/product/Alb
|
||||
var albEndpoint string
|
||||
switch region {
|
||||
case "", "cn-hangzhou-finance":
|
||||
albEndpoint = "alb.cn-hangzhou.aliyuncs.com"
|
||||
default:
|
||||
albEndpoint = fmt.Sprintf("alb.%s.aliyuncs.com", region)
|
||||
}
|
||||
|
||||
albConfig := &aliopen.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
Endpoint: tea.String(albEndpoint),
|
||||
}
|
||||
albClient, err := alialb.NewClient(albConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 接入点一览 https://api.aliyun.com/product/cas
|
||||
var casEndpoint string
|
||||
if !strings.HasPrefix(region, "cn-") {
|
||||
casEndpoint = "cas.ap-southeast-1.aliyuncs.com"
|
||||
} else {
|
||||
casEndpoint = "cas.aliyuncs.com"
|
||||
}
|
||||
|
||||
casConfig := &aliopen.Config{
|
||||
Endpoint: tea.String(casEndpoint),
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
}
|
||||
casClient, err := alicas.NewClient(casConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &wSDKClients{
|
||||
ALB: albClient,
|
||||
CAS: casClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSSLManager(accessKeyId, accessKeySecret, resourceGroupId, region string) (core.SSLManager, error) {
|
||||
casRegion := region
|
||||
if casRegion != "" {
|
||||
// 阿里云 CAS 服务接入点是独立于 ALB 服务的
|
||||
// 国内版固定接入点:华东一杭州
|
||||
// 国际版固定接入点:亚太东南一新加坡
|
||||
if !strings.HasPrefix(casRegion, "cn-") {
|
||||
casRegion = "ap-southeast-1"
|
||||
} else {
|
||||
casRegion = "cn-hangzhou"
|
||||
}
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
ResourceGroupId: resourceGroupId,
|
||||
Region: casRegion,
|
||||
})
|
||||
return sslmgr, err
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package aliyunalb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-alb"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fAccessKeyId string
|
||||
fAccessKeySecret string
|
||||
fRegion string
|
||||
fLoadbalancerId string
|
||||
fListenerId string
|
||||
fDomain string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_SSLDEPLOYER_ALIYUNALB_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
|
||||
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
|
||||
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./aliyun_alb_test.go -args \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_ACCESSKEYID="your-access-key-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_ACCESSKEYSECRET="your-access-key-secret" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_REGION="cn-hangzhou" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_LOADBALANCERID="your-alb-instance-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_LISTENERID="your-alb-listener-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNALB_DOMAIN="your-alb-sni-domain"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy_ToLoadbalancer", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||
fmt.Sprintf("REGION: %v", fRegion),
|
||||
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
|
||||
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
ResourceType: provider.RESOURCE_TYPE_LOADBALANCER,
|
||||
LoadbalancerId: fLoadbalancerId,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
|
||||
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("REGION: %v", fRegion),
|
||||
fmt.Sprintf("LISTENERID: %v", fListenerId),
|
||||
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
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)
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package aliyunalb
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
// 资源类型:部署到指定负载均衡器。
|
||||
RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
|
||||
// 资源类型:部署到指定监听器。
|
||||
RESOURCE_TYPE_LISTENER = ResourceType("listener")
|
||||
)
|
||||
@@ -1,277 +0,0 @@
|
||||
package aliyunapigw
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
aliapig "github.com/alibabacloud-go/apig-20240327/v3/client"
|
||||
alicloudapi "github.com/alibabacloud-go/cloudapi-20160714/v5/client"
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core"
|
||||
sslmgrsp "github.com/usual2970/certimate/internal/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
xtypes "github.com/usual2970/certimate/internal/pkg/utils/types"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
// 阿里云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 阿里云资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 服务类型。
|
||||
ServiceType ServiceType `json:"serviceType"`
|
||||
// API 网关 ID。
|
||||
// 服务类型为 [SERVICE_TYPE_CLOUDNATIVE] 时必填。
|
||||
GatewayId string `json:"gatewayId,omitempty"`
|
||||
// API 分组 ID。
|
||||
// 服务类型为 [SERVICE_TYPE_TRADITIONAL] 时必填。
|
||||
GroupId string `json:"groupId,omitempty"`
|
||||
// 自定义域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClients *wSDKClients
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
type wSDKClients struct {
|
||||
CloudNativeAPIGateway *aliapig.Client
|
||||
TraditionalAPIGateway *alicloudapi.Client
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
|
||||
func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the ssl deployer provider is nil")
|
||||
}
|
||||
|
||||
clients, err := createSDKClients(config.AccessKeyId, config.AccessKeySecret, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := createSSLManager(config.AccessKeyId, config.AccessKeySecret, config.ResourceGroupId, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClients: clients,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
switch d.config.ServiceType {
|
||||
case SERVICE_TYPE_TRADITIONAL:
|
||||
if err := d.deployToTraditional(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case SERVICE_TYPE_CLOUDNATIVE:
|
||||
if err := d.deployToCloudNative(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported service type '%s'", string(d.config.ServiceType))
|
||||
}
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToTraditional(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.GroupId == "" {
|
||||
return errors.New("config `groupId` is required")
|
||||
}
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 为自定义域名添加 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate
|
||||
setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{
|
||||
GroupId: tea.String(d.config.GroupId),
|
||||
DomainName: tea.String(d.config.Domain),
|
||||
CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
|
||||
CertificateBody: tea.String(certPEM),
|
||||
CertificatePrivateKey: tea.String(privkeyPEM),
|
||||
}
|
||||
setDomainCertificateResp, err := d.sdkClients.TraditionalAPIGateway.SetDomainCertificate(setDomainCertificateReq)
|
||||
d.logger.Debug("sdk request 'apigateway.SetDomainCertificate'", slog.Any("request", setDomainCertificateReq), slog.Any("response", setDomainCertificateResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apigateway.SetDomainCertificate': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM string, privkeyPEM string) error {
|
||||
if d.config.GatewayId == "" {
|
||||
return errors.New("config `gatewayId` is required")
|
||||
}
|
||||
if d.config.Domain == "" {
|
||||
return errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 遍历查询域名列表,获取域名 ID
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains
|
||||
var domainId string
|
||||
listDomainsPageNumber := int32(1)
|
||||
listDomainsPageSize := int32(10)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainsReq := &aliapig.ListDomainsRequest{
|
||||
ResourceGroupId: xtypes.ToPtrOrZeroNil(d.config.ResourceGroupId),
|
||||
GatewayId: tea.String(d.config.GatewayId),
|
||||
NameLike: tea.String(d.config.Domain),
|
||||
PageNumber: tea.Int32(listDomainsPageNumber),
|
||||
PageSize: tea.Int32(listDomainsPageSize),
|
||||
}
|
||||
listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomains(listDomainsReq)
|
||||
d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err)
|
||||
}
|
||||
|
||||
if listDomainsResp.Body.Data.Items != nil {
|
||||
for _, domainInfo := range listDomainsResp.Body.Data.Items {
|
||||
if strings.EqualFold(tea.StringValue(domainInfo.Name), d.config.Domain) {
|
||||
domainId = tea.StringValue(domainInfo.DomainId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if domainId != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if listDomainsResp.Body.Data.Items == nil || len(listDomainsResp.Body.Data.Items) < int(listDomainsPageSize) {
|
||||
break
|
||||
} else {
|
||||
listDomainsPageNumber++
|
||||
}
|
||||
}
|
||||
if domainId == "" {
|
||||
return errors.New("domain not found")
|
||||
}
|
||||
|
||||
// 查询域名
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain
|
||||
getDomainReq := &aliapig.GetDomainRequest{}
|
||||
getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomain(tea.String(domainId), getDomainReq)
|
||||
d.logger.Debug("sdk request 'apig.GetDomain'", slog.Any("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err)
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 更新域名
|
||||
// REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain
|
||||
updateDomainReq := &aliapig.UpdateDomainRequest{
|
||||
Protocol: tea.String("HTTPS"),
|
||||
ForceHttps: getDomainResp.Body.Data.ForceHttps,
|
||||
MTLSEnabled: getDomainResp.Body.Data.MTLSEnabled,
|
||||
Http2Option: getDomainResp.Body.Data.Http2Option,
|
||||
TlsMin: getDomainResp.Body.Data.TlsMin,
|
||||
TlsMax: getDomainResp.Body.Data.TlsMax,
|
||||
TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig,
|
||||
CertIdentifier: tea.String(upres.ExtendedData["certIdentifier"].(string)),
|
||||
}
|
||||
updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomain(tea.String(domainId), updateDomainReq)
|
||||
d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.Any("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClients(accessKeyId, accessKeySecret, region string) (*wSDKClients, error) {
|
||||
// 接入点一览 https://api.aliyun.com/product/APIG
|
||||
cloudNativeAPIGEndpoint := strings.ReplaceAll(fmt.Sprintf("apig.%s.aliyuncs.com", region), "..", ".")
|
||||
cloudNativeAPIGConfig := &aliopen.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
Endpoint: tea.String(cloudNativeAPIGEndpoint),
|
||||
}
|
||||
cloudNativeAPIGClient, err := aliapig.NewClient(cloudNativeAPIGConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 接入点一览 https://api.aliyun.com/product/CloudAPI
|
||||
traditionalAPIGEndpoint := strings.ReplaceAll(fmt.Sprintf("apigateway.%s.aliyuncs.com", region), "..", ".")
|
||||
traditionalAPIGConfig := &aliopen.Config{
|
||||
AccessKeyId: tea.String(accessKeyId),
|
||||
AccessKeySecret: tea.String(accessKeySecret),
|
||||
Endpoint: tea.String(traditionalAPIGEndpoint),
|
||||
}
|
||||
traditionalAPIGClient, err := alicloudapi.NewClient(traditionalAPIGConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &wSDKClients{
|
||||
CloudNativeAPIGateway: cloudNativeAPIGClient,
|
||||
TraditionalAPIGateway: traditionalAPIGClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSSLManager(accessKeyId, accessKeySecret, resourceGroupId, region string) (core.SSLManager, error) {
|
||||
casRegion := region
|
||||
if casRegion != "" {
|
||||
// 阿里云 CAS 服务接入点是独立于 APIGateway 服务的
|
||||
// 国内版固定接入点:华东一杭州
|
||||
// 国际版固定接入点:亚太东南一新加坡
|
||||
if !strings.HasPrefix(casRegion, "cn-") {
|
||||
casRegion = "ap-southeast-1"
|
||||
} else {
|
||||
casRegion = "cn-hangzhou"
|
||||
}
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
AccessKeyId: accessKeyId,
|
||||
AccessKeySecret: accessKeySecret,
|
||||
ResourceGroupId: resourceGroupId,
|
||||
Region: casRegion,
|
||||
})
|
||||
return sslmgr, err
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package aliyunapigw_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/usual2970/certimate/internal/pkg/core/ssl-deployer/providers/aliyun-apigw"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fAccessKeyId string
|
||||
fAccessKeySecret string
|
||||
fRegion string
|
||||
fServiceType string
|
||||
fGatewayId string
|
||||
fGroupId string
|
||||
fDomain string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
|
||||
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
|
||||
flag.StringVar(&fGatewayId, argsPrefix+"GATEWARYID", "", "")
|
||||
flag.StringVar(&fGroupId, argsPrefix+"GROUPID", "", "")
|
||||
flag.StringVar(&fServiceType, argsPrefix+"SERVICETYPE", "", "")
|
||||
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./aliyun_apigw_test.go -args \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_ACCESSKEYID="your-access-key-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_ACCESSKEYSECRET="your-access-key-secret" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_REGION="cn-hangzhou" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_GATEWAYID="your-api-gateway-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_GROUPID="your-api-group-id" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_SERVICETYPE="cloudnative" \
|
||||
--CERTIMATE_SSLDEPLOYER_ALIYUNAPIGW_DOMAIN="example.com"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
|
||||
fmt.Sprintf("REGION: %v", fRegion),
|
||||
fmt.Sprintf("GATEWAYID: %v", fGatewayId),
|
||||
fmt.Sprintf("GROUPID: %v", fGroupId),
|
||||
fmt.Sprintf("SERVICETYPE: %v", fServiceType),
|
||||
fmt.Sprintf("DOMAIN: %v", fDomain),
|
||||
}, "\n"))
|
||||
|
||||
deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeySecret: fAccessKeySecret,
|
||||
Region: fRegion,
|
||||
ServiceType: provider.ServiceType(fServiceType),
|
||||
GatewayId: fGatewayId,
|
||||
GroupId: fGroupId,
|
||||
Domain: fDomain,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user