refactor: re-implement cloudflare services with custom sdk, to lightweight package size

This commit is contained in:
Fu Diwei 2026-06-10 23:13:21 +08:00
parent 5a68915cf0
commit 12cb2bbced
9 changed files with 326 additions and 38 deletions

4
go.mod
View File

@ -44,7 +44,6 @@ require (
github.com/baidubce/bce-sdk-go v0.9.268
github.com/byteplus-sdk/byteplus-go-sdk-v2 v1.0.66
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.68
github.com/cloudflare/cloudflare-go/v7 v7.4.0
github.com/go-acme/lego/v5 v5.2.2
github.com/go-cmd/cmd v1.4.3
github.com/go-resty/resty/v2 v2.17.2
@ -59,7 +58,6 @@ require (
github.com/minio/minio-go/v7 v7.2.0
github.com/mohuatech/mohuacloud-go-sdk v0.0.0-20251115182757-6fba4d0a4c47
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.10
github.com/pocketbase/dbx v1.12.0
github.com/pocketbase/pocketbase v0.39.3
@ -269,10 +267,10 @@ require (
github.com/nrdcg/namesilo v0.5.0 // indirect
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/tinylib/msgp v1.6.4 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect

5
go.sum
View File

@ -298,8 +298,6 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go/v7 v7.4.0 h1:JdTxzeXcAhtJ9rUkNISK4ABA55pZP8HLxx6XsPSA7dU=
github.com/cloudflare/cloudflare-go/v7 v7.4.0/go.mod h1:9zcoIAtu6cmcoPszCNISvqYMXs8wObtVGXE1qGFMrNU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@ -957,7 +955,6 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.112 h1:vxOJOer
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.3.112/go.mod h1:zCS+AwYqW2r0RBeCWHh9dmydCmP6LSew6y9Uyxsedy0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.111 h1:CKRs1TWBVinSLGZ2J0OVtfzoqoWMizrJ6IdYo6QEfow=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.3.111/go.mod h1:AfaoGamQm8lafjz+jkZn+T9VE1+tZ3ul9V9v6PORMVE=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
@ -966,8 +963,6 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=

View File

@ -6,12 +6,10 @@ import (
"fmt"
"log/slog"
cf "github.com/cloudflare/cloudflare-go/v7"
cfcertificates "github.com/cloudflare/cloudflare-go/v7/custom_certificates"
cfhostnames "github.com/cloudflare/cloudflare-go/v7/custom_hostnames"
cfoption "github.com/cloudflare/cloudflare-go/v7/option"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
cloudflaresdk "github.com/certimate-go/certimate/pkg/sdk3rd/cloudflare"
)
type (
@ -36,7 +34,7 @@ type DeployerConfig struct {
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *cfcertificates.CustomCertificateService
sdkClient *cloudflaresdk.Client
}
var _ Provider = (*Deployer)(nil)
@ -74,29 +72,30 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
if d.config.CertificateId == "" {
// 新建自定义证书
// REF: https://developers.cf.com/api/resources/custom_certificates/methods/create
customCertificateNewReq := cfcertificates.CustomCertificateNewParams{
ZoneID: cf.F(d.config.ZoneId),
Certificate: cf.F(certPEM),
PrivateKey: cf.F(privkeyPEM),
BundleMethod: cf.F(cfhostnames.BundleMethodUbiquitous),
Deploy: cf.F(cfcertificates.CustomCertificateNewParamsDeploy(cmp.Or(d.config.Environment, "production"))),
customCertificateCreateReq := &cloudflaresdk.CustomCertificateCreateRequest{
ZoneId: d.config.ZoneId,
Certificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
BundleMethod: lo.ToPtr("ubiquitous"),
Deploy: lo.ToPtr(cmp.Or(d.config.Environment, "production")),
}
customCertificateNewResp, err := d.sdkClient.New(ctx, customCertificateNewReq)
d.logger.Debug("sdk request 'CustomCertificates.New'", slog.Any("request", customCertificateNewReq), slog.Any("response", customCertificateNewResp))
customCertificateCreateResp, err := d.sdkClient.CustomCertificateCreateWithContext(ctx, customCertificateCreateReq)
d.logger.Debug("sdk request 'CustomCertificates.Create'", slog.Any("request", customCertificateCreateReq), slog.Any("response", customCertificateCreateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'CustomCertificates.New': %w", err)
return nil, fmt.Errorf("failed to execute sdk request 'CustomCertificates.Create': %w", err)
}
} else {
// 编辑自定义证书
// REF: https://developers.cloudflare.com/api/resources/custom_certificates/methods/edit
customCertificateEditReq := cfcertificates.CustomCertificateEditParams{
ZoneID: cf.F(d.config.ZoneId),
Certificate: cf.F(certPEM),
PrivateKey: cf.F(privkeyPEM),
BundleMethod: cf.F(cfhostnames.BundleMethodUbiquitous),
Deploy: cf.F(cfcertificates.CustomCertificateEditParamsDeploy(cmp.Or(d.config.Environment, "production"))),
customCertificateEditReq := &cloudflaresdk.CustomCertificateEditRequest{
ZoneId: d.config.ZoneId,
CertificateId: d.config.CertificateId,
Certificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
BundleMethod: lo.ToPtr("ubiquitous"),
Deploy: lo.ToPtr(cmp.Or(d.config.Environment, "production")),
}
customCertificateEditResp, err := d.sdkClient.Edit(ctx, d.config.CertificateId, customCertificateEditReq)
customCertificateEditResp, err := d.sdkClient.CustomCertificateEditWithContext(ctx, customCertificateEditReq)
d.logger.Debug("sdk request 'CustomCertificates.Edit'", slog.Any("request", customCertificateEditReq), slog.Any("response", customCertificateEditResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'CustomCertificates.Edit': %w", err)
@ -106,14 +105,13 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
return &DeployResult{}, nil
}
func createSDKClient(apiToken string) (*cfcertificates.CustomCertificateService, error) {
if apiToken == "" {
return nil, fmt.Errorf("cloudflare: invalid api token")
func createSDKClient(apiToken string) (*cloudflaresdk.Client, error) {
client, err := cloudflaresdk.NewClient(
cloudflaresdk.WithApiToken(apiToken),
)
if err != nil {
return nil, err
}
opts := append(cf.DefaultClientOptions(), cfoption.WithAPIToken(apiToken))
srv := cfcertificates.NewCustomCertificateService(opts...)
return srv, nil
return client, err
}

View File

@ -2,11 +2,11 @@ package vod
import (
"encoding/json"
"errors"
"net/http"
"net/url"
"strconv"
"github.com/pkg/errors"
"github.com/volcengine/volc-sdk-golang/service/vod/models/request"
"github.com/volcengine/volc-sdk-golang/service/vod/models/response"
"google.golang.org/protobuf/encoding/protojson"

View File

@ -0,0 +1,48 @@
package cloudflare
import (
"context"
"fmt"
"net/http"
"net/url"
)
type CustomCertificateCreateRequest struct {
ZoneId string `json:"-"`
CustomCsrId *string `json:"custom_csr_id,omitempty"`
Certificate *string `json:"certificate,omitempty"`
PrivateKey *string `json:"private_key,omitempty"`
BundleMethod *string `json:"bundle_method,omitempty"`
Type *string `json:"type,omitempty"`
Deploy *string `json:"deploy,omitempty"`
Policy *string `json:"policy,omitempty"`
GeoRestrictions []*GeoRestriction `json:"geo_restrictions,omitempty"`
}
type CustomCertificateCreateResponse struct {
sdkResponseBase
Result *CustomCertificate `json:"result,omitempty"`
}
func (c *Client) CustomCertificateCreate(req *CustomCertificateCreateRequest) (*CustomCertificateCreateResponse, error) {
return c.CustomCertificateCreateWithContext(context.Background(), req)
}
func (c *Client) CustomCertificateCreateWithContext(ctx context.Context, req *CustomCertificateCreateRequest) (*CustomCertificateCreateResponse, error) {
path := fmt.Sprintf("/zones/%s/custom_certificates", url.PathEscape(req.ZoneId))
httpreq, err := c.newRequest(http.MethodPost, path)
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &CustomCertificateCreateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,48 @@
package cloudflare
import (
"context"
"fmt"
"net/http"
"net/url"
)
type CustomCertificateEditRequest struct {
ZoneId string `json:"-"`
CertificateId string `json:"-"`
CustomCsrId *string `json:"custom_csr_id,omitempty"`
Certificate *string `json:"certificate,omitempty"`
PrivateKey *string `json:"private_key,omitempty"`
BundleMethod *string `json:"bundle_method,omitempty"`
Deploy *string `json:"deploy,omitempty"`
Policy *string `json:"policy,omitempty"`
GeoRestrictions []*GeoRestriction `json:"geo_restrictions,omitempty"`
}
type CustomCertificateEditResponse struct {
sdkResponseBase
Result *CustomCertificate `json:"result,omitempty"`
}
func (c *Client) CustomCertificateEdit(req *CustomCertificateEditRequest) (*CustomCertificateEditResponse, error) {
return c.CustomCertificateEditWithContext(context.Background(), req)
}
func (c *Client) CustomCertificateEditWithContext(ctx context.Context, req *CustomCertificateEditRequest) (*CustomCertificateEditResponse, error) {
path := fmt.Sprintf("/zones/%s/custom_certificates/%s", url.PathEscape(req.ZoneId), url.PathEscape(req.CertificateId))
httpreq, err := c.newRequest(http.MethodPatch, path)
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &CustomCertificateEditResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,104 @@
package cloudflare
import (
"crypto/tls"
"encoding/json"
"fmt"
"time"
"github.com/go-resty/resty/v2"
"github.com/certimate-go/certimate/internal/app"
)
type Client struct {
rc *resty.Client
}
func NewClient(optFns ...OptionsFunc) (*Client, error) {
opts := &Options{}
for _, fn := range optFns {
fn(opts)
}
if opts.ApiToken == "" {
return nil, fmt.Errorf("sdkerr: unset apiToken")
}
restyClient := resty.New().
SetBaseURL("https://api.cloudflare.com/client/v4").
SetHeader("Accept", "application/json").
SetHeader("Authorization", "Bearer "+opts.ApiToken).
SetHeader("Content-Type", "application/json").
SetHeader("User-Agent", app.AppUserAgent)
return &Client{rc: restyClient}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.rc.SetTimeout(timeout)
return c
}
func (c *Client) SetTLSConfig(config *tls.Config) *Client {
c.rc.SetTLSClientConfig(config)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
if method == "" {
return nil, fmt.Errorf("sdkerr: unset method")
}
if path == "" {
return nil, fmt.Errorf("sdkerr: unset path")
}
req := c.rc.R()
req.Method = method
req.URL = path
return req, nil
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
// WARN:
// PLEASE DO NOT USE `req.SetResult` or `req.SetError` HERE! USE `doRequestWithResult` INSTEAD.
resp, err := req.Send()
if err != nil {
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
} else if resp.IsError() {
return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String())
}
return resp, nil
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := c.doRequest(req)
if err != nil {
if resp != nil {
json.Unmarshal(resp.Body(), &res)
}
return resp, err
}
if len(resp.Body()) != 0 {
if err := json.Unmarshal(resp.Body(), &res); err != nil {
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String())
} else {
if rErrors := res.GetErrors(); rErrors != nil {
return resp, fmt.Errorf("sdkerr: errors='%s'", rErrors.Error())
}
}
}
return resp, nil
}

View File

@ -0,0 +1,13 @@
package cloudflare
type Options struct {
ApiToken string
}
type OptionsFunc func(*Options)
func WithApiToken(apiToken string) OptionsFunc {
return func(o *Options) {
o.ApiToken = apiToken
}
}

View File

@ -0,0 +1,84 @@
package cloudflare
import (
"fmt"
"strings"
)
type sdkResponse interface {
GetErrors() error
GetSuccess() bool
}
type sdkResponseBase struct {
Errors APIErrors `json:"errors,omitempty"`
Messages []APIMessage `json:"messages,omitempty"`
Success bool `json:"success,omitempty"`
}
type APIMessage struct {
Code int `json:"code"`
Message string `json:"message"`
DocumentationURL string `json:"documentation_url"`
ErrorChain []APIErrorChain `json:"error_chain"`
Source *APISource `json:"source"`
}
type APIErrors []APIMessage
type APIErrorChain struct {
Code int `json:"code"`
Message string `json:"message"`
}
type APISource struct {
Pointer string `json:"pointer"`
}
func (e APIErrors) Error() string {
builder := &strings.Builder{}
for _, item := range e {
fmt.Fprintf(builder, "%d: %s", item.Code, item.Message)
for _, link := range item.ErrorChain {
fmt.Fprintf(builder, "; %d: %s", link.Code, link.Message)
}
}
return builder.String()
}
func (r *sdkResponseBase) GetErrors() error {
if len(r.Errors) > 0 {
return r.Errors
}
return nil
}
func (r *sdkResponseBase) GetSuccess() bool {
return r.Success
}
var _ sdkResponse = (*sdkResponseBase)(nil)
type GeoRestriction struct {
Label string `json:"label"`
}
type CustomCertificate struct {
ID string `json:"id"`
ZoneID string `json:"zone_id"`
BundleMethod string `json:"bundle_method"`
CustomCsrID string `json:"custom_csr_id"`
GeoRestrictions []GeoRestriction `json:"geo_restrictions"`
Hosts []string `json:"hosts"`
Issuer string `json:"issuer"`
PolicyRestrictions string `json:"policy_restrictions"`
Priority float64 `json:"priority"`
Signature string `json:"signature"`
Status string `json:"status"`
ExpiresOn string `json:"expires_on"`
UploadedOn string `json:"uploaded_on"`
ModifiedOn string `json:"modified_on"`
}