refactor: clean code

This commit is contained in:
Fu Diwei 2025-04-24 10:46:16 +08:00
parent 034bb71b10
commit 2d17501072
4 changed files with 111 additions and 104 deletions

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -22,7 +23,7 @@ import (
"github.com/usual2970/certimate/internal/repository" "github.com/usual2970/certimate/internal/repository"
) )
type ApplyCertResult struct { type ApplyResult struct {
CertificateFullChain string CertificateFullChain string
IssuerCertificate string IssuerCertificate string
PrivateKey string PrivateKey string
@ -33,34 +34,24 @@ type ApplyCertResult struct {
} }
type Applicant interface { type Applicant interface {
Apply() (*ApplyCertResult, error) Apply(ctx context.Context) (*ApplyResult, error)
} }
type applicantOptions struct { type ApplicantWithWorkflowNodeConfig struct {
Domains []string Node *domain.WorkflowNode
ContactEmail string Logger *slog.Logger
Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any
ProviderExtendedConfig map[string]any
CAProvider domain.ApplyCAProviderType
CAProviderAccessConfig map[string]any
CAProviderExtendedConfig map[string]any
KeyAlgorithm string
Nameservers []string
DnsPropagationTimeout int32
DnsTTL int32
DisableFollowCNAME bool
ReplacedARIAcct string
ReplacedARICert string
} }
func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) { func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, error) {
if node.Type != domain.WorkflowNodeTypeApply { if config.Node == nil {
return nil, fmt.Errorf("node is nil")
}
if config.Node.Type != domain.WorkflowNodeTypeApply {
return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply)) return nil, fmt.Errorf("node type is not '%s'", string(domain.WorkflowNodeTypeApply))
} }
nodeConfig := node.GetConfigForApply() nodeConfig := config.Node.GetConfigForApply()
options := &applicantOptions{ options := &applicantProviderOptions{
Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }),
ContactEmail: nodeConfig.ContactEmail, ContactEmail: nodeConfig.ContactEmail,
Provider: domain.ApplyDNSProviderType(nodeConfig.Provider), Provider: domain.ApplyDNSProviderType(nodeConfig.Provider),
@ -113,7 +104,7 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
} }
certRepo := repository.NewCertificateRepository() certRepo := repository.NewCertificateRepository()
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), node.Id) lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id)
if lastCertificate != nil { if lastCertificate != nil {
newCertSan := slices.Clone(options.Domains) newCertSan := slices.Clone(options.Domains)
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";") oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
@ -130,18 +121,46 @@ func NewWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
} }
} }
applicant, err := createApplicant(options) applicant, err := createApplicantProvider(options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &proxyApplicant{ return &applicantImpl{
applicant: applicant, applicant: applicant,
options: options, options: options,
}, nil }, nil
} }
func apply(challengeProvider challenge.Provider, options *applicantOptions) (*ApplyCertResult, error) { type applicantImpl struct {
applicant challenge.Provider
options *applicantProviderOptions
}
var _ Applicant = (*applicantImpl)(nil)
func (d *applicantImpl) Apply(ctx context.Context) (*ApplyResult, error) {
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
if err := limiter.Wait(ctx); err != nil {
return nil, err
}
return applyUseLego(d.applicant, d.options)
}
const (
limitBurst = 300
limitRate float64 = float64(1) / float64(36)
)
var limiters sync.Map
func getLimiter(key string) *rate.Limiter {
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
return limiter.(*rate.Limiter)
}
func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) {
user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail) user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail)
if err != nil { if err != nil {
return nil, err return nil, err
@ -153,7 +172,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
// Create an ACME client config // Create an ACME client config
config := lego.NewConfig(user) config := lego.NewConfig(user)
config.Certificate.KeyType = parseKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm)) config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm))
config.CADirURL = sslProviderUrls[user.CA] config.CADirURL = sslProviderUrls[user.CA]
if user.CA == sslProviderSSLCom { if user.CA == sslProviderSSLCom {
if strings.HasPrefix(options.KeyAlgorithm, "RSA") { if strings.HasPrefix(options.KeyAlgorithm, "RSA") {
@ -175,7 +194,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers))) challengeOptions = append(challengeOptions, dns01.AddRecursiveNameservers(dns01.ParseNameservers(options.Nameservers)))
challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement()) challengeOptions = append(challengeOptions, dns01.DisableAuthoritativeNssPropagationRequirement())
} }
client.Challenge.SetDNS01Provider(challengeProvider, challengeOptions...) client.Challenge.SetDNS01Provider(legoProvider, challengeOptions...)
// New users need to register first // New users need to register first
if !user.hasRegistration() { if !user.hasRegistration() {
@ -199,7 +218,7 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
return nil, err return nil, err
} }
return &ApplyCertResult{ return &ApplyResult{
CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)), CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)),
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
@ -210,47 +229,20 @@ func apply(challengeProvider challenge.Provider, options *applicantOptions) (*Ap
}, nil }, nil
} }
func parseKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType { func parseLegoKeyAlgorithm(algo domain.CertificateKeyAlgorithmType) certcrypto.KeyType {
switch algo { alogMap := map[domain.CertificateKeyAlgorithmType]certcrypto.KeyType{
case domain.CertificateKeyAlgorithmTypeRSA2048: domain.CertificateKeyAlgorithmTypeRSA2048: certcrypto.RSA2048,
return certcrypto.RSA2048 domain.CertificateKeyAlgorithmTypeRSA3072: certcrypto.RSA3072,
case domain.CertificateKeyAlgorithmTypeRSA3072: domain.CertificateKeyAlgorithmTypeRSA4096: certcrypto.RSA4096,
return certcrypto.RSA3072 domain.CertificateKeyAlgorithmTypeRSA8192: certcrypto.RSA8192,
case domain.CertificateKeyAlgorithmTypeRSA4096: domain.CertificateKeyAlgorithmTypeEC256: certcrypto.EC256,
return certcrypto.RSA4096 domain.CertificateKeyAlgorithmTypeEC384: certcrypto.EC384,
case domain.CertificateKeyAlgorithmTypeRSA8192: domain.CertificateKeyAlgorithmTypeEC512: certcrypto.KeyType("P512"),
return certcrypto.RSA8192 }
case domain.CertificateKeyAlgorithmTypeEC256:
return certcrypto.EC256 if keyType, ok := alogMap[algo]; ok {
case domain.CertificateKeyAlgorithmTypeEC384: return keyType
return certcrypto.EC384
case domain.CertificateKeyAlgorithmTypeEC512:
return certcrypto.KeyType("P512")
} }
return certcrypto.RSA2048 return certcrypto.RSA2048
} }
// TODO: 暂时使用代理模式以兼容之前版本代码,后续重新实现此处逻辑
type proxyApplicant struct {
applicant challenge.Provider
options *applicantOptions
}
var limiters sync.Map
const (
limitBurst = 300
limitRate float64 = float64(1) / float64(36)
)
func getLimiter(key string) *rate.Limiter {
limiter, _ := limiters.LoadOrStore(key, rate.NewLimiter(rate.Limit(limitRate), 300))
return limiter.(*rate.Limiter)
}
func (d *proxyApplicant) Apply() (*ApplyCertResult, error) {
limiter := getLimiter(fmt.Sprintf("apply_%s", d.options.ContactEmail))
limiter.Wait(context.Background())
return apply(d.applicant, d.options)
}

View File

@ -38,7 +38,25 @@ import (
maputil "github.com/usual2970/certimate/internal/pkg/utils/map" maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
) )
func createApplicant(options *applicantOptions) (challenge.Provider, error) { type applicantProviderOptions struct {
Domains []string
ContactEmail string
Provider domain.ApplyDNSProviderType
ProviderAccessConfig map[string]any
ProviderExtendedConfig map[string]any
CAProvider domain.ApplyCAProviderType
CAProviderAccessConfig map[string]any
CAProviderExtendedConfig map[string]any
KeyAlgorithm string
Nameservers []string
DnsPropagationTimeout int32
DnsTTL int32
DisableFollowCNAME bool
ReplacedARIAcct string
ReplacedARICert string
}
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
/* /*
注意如果追加新的常量值请保持以 ASCII 排序 注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order. NOTICE: If you add new constant, please keep ASCII order.

View File

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

View File

@ -82,7 +82,13 @@ import (
sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice"
) )
func createDeployer(options *deployerOptions) (deployer.Deployer, error) { type deployerProviderOptions struct {
Provider domain.DeployProviderType
ProviderAccessConfig map[string]any
ProviderDeployConfig map[string]any
}
func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer, error) {
/* /*
注意如果追加新的常量值请保持以 ASCII 排序 注意如果追加新的常量值请保持以 ASCII 排序
NOTICE: If you add new constant, please keep ASCII order. NOTICE: If you add new constant, please keep ASCII order.