mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
refactor: clean code
This commit is contained in:
parent
034bb71b10
commit
2d17501072
@ -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)
|
|
||||||
}
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user