From b8997512f97bf6b08ad6a01f75c6c67b9e981c04 Mon Sep 17 00:00:00 2001 From: xuans Date: Wed, 30 Sep 2026 02:17:44 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=BD=90=E8=AF=81=E4=B9=A6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接入证书导入、恢复和校验任务流程,保持现有应用与 HTTP 依赖兼容。 --- backend/internal/app/app.go | 8 +- backend/internal/app/certificate_tasks.go | 23 ++++ backend/internal/app/certificate_workflow.go | 36 ++++++ backend/internal/httpapi/server.go | 115 ++++++++++--------- 4 files changed, 124 insertions(+), 58 deletions(-) diff --git a/backend/internal/app/app.go b/backend/internal/app/app.go index 8657755..6877e70 100644 --- a/backend/internal/app/app.go +++ b/backend/internal/app/app.go @@ -170,7 +170,7 @@ func NewWithConfigPath(ctx context.Context, cfg config.Config, configPath string return certificate.RollbackSavedResource(ctx, saved) }, ImportCertificate: func(ctx context.Context, request certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) { - result, err := workflow.Import(ctx, request) + result, err := trackedWorkflow.Import(ctx, request) return result.Certificate, result.Deployment, err }, DeployCertificate: func(ctx context.Context, domainID string) (certificate.IssueResult, deployment.Report, error) { @@ -178,7 +178,11 @@ func NewWithConfigPath(ctx context.Context, cfg config.Config, configPath string return result.Certificate, result.Deployment, err }, RestoreCertificateVersion: func(ctx context.Context, domainID, version string) (certificate.IssueResult, error) { - return certificates.RestoreVersion(ctx, domainID, version) + result, err := trackedWorkflow.Restore(ctx, domainID, version) + return result.Certificate, err + }, + ValidateCertificate: func(ctx context.Context, domainID string) (certificate.IssueResult, certificate.ValidationResult, error) { + return trackedWorkflow.Validate(ctx, domainID) }, RunRenewal: func(ctx context.Context) (certificate.RenewalReport, error) { return renewal.RunOnce(ctx) diff --git a/backend/internal/app/certificate_tasks.go b/backend/internal/app/certificate_tasks.go index 6ebf5d4..d960dfc 100644 --- a/backend/internal/app/certificate_tasks.go +++ b/backend/internal/app/certificate_tasks.go @@ -34,6 +34,29 @@ func (w *certificateTaskWorkflow) DeployCurrent(ctx context.Context, id string) return w.run(ctx, id, "certificate_deploy", "certificate_deploy", 1, 0, w.workflow.DeployCurrent) } +func (w *certificateTaskWorkflow) Import(ctx context.Context, request certificate.ImportRequest) (CertificateWorkflowResult, error) { + return w.run(ctx, request.DomainID, "certificate_import", "certificate_import", 1, 0, func(ctx context.Context, _ string) (CertificateWorkflowResult, error) { + return w.workflow.Import(ctx, request) + }) +} + +func (w *certificateTaskWorkflow) Restore(ctx context.Context, domainID, version string) (CertificateWorkflowResult, error) { + return w.run(ctx, domainID, "certificate_restore", "certificate_restore", 1, 0, func(ctx context.Context, id string) (CertificateWorkflowResult, error) { + result, err := w.workflow.RestoreVersion(ctx, id, version) + return CertificateWorkflowResult{Certificate: result}, err + }) +} + +func (w *certificateTaskWorkflow) Validate(ctx context.Context, domainID string) (certificate.IssueResult, certificate.ValidationResult, error) { + var validation certificate.ValidationResult + result, err := w.run(ctx, domainID, "certificate_validate", "certificate_validate", 1, 0, func(ctx context.Context, id string) (CertificateWorkflowResult, error) { + certificateResult, validationResult, err := w.workflow.ValidateCurrent(ctx, id) + validation = validationResult + return CertificateWorkflowResult{Certificate: certificateResult}, err + }) + return result.Certificate, validation, err +} + func (w *certificateTaskWorkflow) run(ctx context.Context, id, taskType, stage string, attempts int, interval time.Duration, execute func(context.Context, string) (CertificateWorkflowResult, error)) (result CertificateWorkflowResult, resultErr error) { if w.tasks == nil || w.audit == nil { return result, errors.New("证书任务记录服务未初始化") diff --git a/backend/internal/app/certificate_workflow.go b/backend/internal/app/certificate_workflow.go index 9a8d3cc..3f67427 100644 --- a/backend/internal/app/certificate_workflow.go +++ b/backend/internal/app/certificate_workflow.go @@ -145,6 +145,42 @@ func (w *CertificateWorkflow) Import(ctx context.Context, request certificate.Im return CertificateWorkflowResult{Certificate: certificateResult, Deployment: report}, err } +// RestoreVersion restores a certificate version through the certificate service. +func (w *CertificateWorkflow) RestoreVersion(ctx context.Context, domainID, version string) (certificate.IssueResult, error) { + if w == nil || w.Certificates == nil { + return certificate.IssueResult{}, errors.New("证书恢复流程未初始化") + } + restorer, ok := w.Certificates.(interface { + RestoreVersion(context.Context, string, string) (certificate.IssueResult, error) + }) + if !ok { + return certificate.IssueResult{}, errors.New("证书恢复服务未配置") + } + return restorer.RestoreVersion(ctx, domainID, version) +} + +// ValidateCurrent validates the active certificate through the workflow boundary. +func (w *CertificateWorkflow) ValidateCurrent(ctx context.Context, domainID string) (certificate.IssueResult, certificate.ValidationResult, error) { + if w == nil || w.CurrentCertificate == nil { + return certificate.IssueResult{}, certificate.ValidationResult{}, errors.New("证书校验流程未初始化") + } + result, err := w.CurrentCertificate(ctx, domainID) + if err != nil { + return certificate.IssueResult{}, certificate.ValidationResult{}, err + } + validation, err := (certificate.LocalValidator{VerifyChain: true}).ValidateFiles( + ctx, + result.Saved.CertificatePath, + result.Saved.FullChainPath, + result.Saved.PrivateKeyPath, + result.Domains, + ) + if err != nil { + return certificate.IssueResult{}, certificate.ValidationResult{}, fmt.Errorf("当前证书校验失败:%w", err) + } + return result, validation, nil +} + func (w *CertificateWorkflow) notify(ctx context.Context, event notification.Event) { if w == nil || w.Notifier == nil { return diff --git a/backend/internal/httpapi/server.go b/backend/internal/httpapi/server.go index 6dde601..cca2757 100644 --- a/backend/internal/httpapi/server.go +++ b/backend/internal/httpapi/server.go @@ -64,6 +64,7 @@ type Dependencies struct { ImportCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) DeployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error) RestoreCertificateVersion func(context.Context, string, string) (certificate.IssueResult, error) + ValidateCertificate func(context.Context, string) (certificate.IssueResult, certificate.ValidationResult, error) RunRenewal func(context.Context) (certificate.RenewalReport, error) UpdateRenewalPolicy func(certificate.RenewalPolicy) @@ -76,31 +77,32 @@ type Dependencies struct { } type Server struct { - configMu sync.RWMutex - config config.Config - configPath string - frontendDir string - logger *logging.Logger - repository repository.DomainRepository - servers repository.ServerRepository - users repository.UserRepository - projectMembers repository.ProjectMemberRepository - projectDirectory repository.ProjectDirectoryRepository - projectLifecycle repository.ProjectLifecycleRepository - projectResources repository.ProjectResourceReader - projectBindings repository.ProjectBindingRepository - projectDomains repository.ProjectDomainRepository - sshKeys *sshkey.Service - deploymentFactory deployment.ClientFactory - setupDNS setupDNSVerifier - secrets setupSecretWriter - firewall firewall.Manager - issueCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error) - issueSetupCertificate func(context.Context, certificate.SetupCertificateRequest) (certificate.SavedResource, error) - rollbackSetupCertificate func(context.Context, certificate.SavedResource) error - importCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) - deployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error) - restoreVersion func(context.Context, string, string) (certificate.IssueResult, error) + configMu sync.RWMutex + config config.Config + configPath string + frontendDir string + logger *logging.Logger + repository repository.DomainRepository + servers repository.ServerRepository + users repository.UserRepository + projectMembers repository.ProjectMemberRepository + projectDirectory repository.ProjectDirectoryRepository + projectLifecycle repository.ProjectLifecycleRepository + projectResources repository.ProjectResourceReader + projectBindings repository.ProjectBindingRepository + projectDomains repository.ProjectDomainRepository + sshKeys *sshkey.Service + deploymentFactory deployment.ClientFactory + setupDNS setupDNSVerifier + secrets setupSecretWriter + firewall firewall.Manager + issueCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error) + issueSetupCertificate func(context.Context, certificate.SetupCertificateRequest) (certificate.SavedResource, error) + rollbackSetupCertificate func(context.Context, certificate.SavedResource) error + importCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) + deployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error) + restoreVersion func(context.Context, string, string) (certificate.IssueResult, error) + validateCertificateWorkflow func(context.Context, string) (certificate.IssueResult, certificate.ValidationResult, error) runRenewal func(context.Context) (certificate.RenewalReport, error) updateRenewalPolicy func(certificate.RenewalPolicy) @@ -148,37 +150,38 @@ func New(deps Dependencies) *Server { httpListening: make(chan struct{}), config: deps.Config, configPath: deps.ConfigPath, frontendDir: filepath.Clean(frontendDir), logger: deps.Logger, repository: deps.Repository, setupDNS: deps.SetupDNS, secrets: deps.Secrets, firewall: deps.Firewall, router: router, - servers: deps.Servers, - users: deps.Users, - projectMembers: deps.ProjectMembers, - projectDirectory: deps.ProjectDirectory, - projectLifecycle: deps.ProjectLifecycle, - projectResources: deps.ProjectResources, - projectBindings: deps.ProjectBindings, - projectDomains: deps.ProjectDomains, - sshKeys: deps.SSHKeys, - deploymentFactory: deps.DeploymentFactory, - issueCertificate: deps.IssueCertificate, - issueSetupCertificate: deps.IssueSetupCertificate, - rollbackSetupCertificate: deps.RollbackSetupCertificate, - importCertificate: deps.ImportCertificate, - deployCertificate: deps.DeployCertificate, - restoreVersion: deps.RestoreCertificateVersion, - runRenewal: deps.RunRenewal, - updateRenewalPolicy: deps.UpdateRenewalPolicy, - currentCertificate: deps.CurrentCertificate, - resetStatePath: deps.ResetStatePath, - audit: deps.Audit, - tasks: deps.Tasks, - readinessCheck: deps.ReadinessCheck, - aliyunChecker: deps.AliyunChecker, - taskCancels: make(map[string]context.CancelFunc), - resetLimiter: newResetRateLimiter(), - authSessions: make(map[string]authSession), - loginAttempts: make(map[string]loginAttempt), - loginSources: make(map[string]loginSource), - loginPending: make(map[string]int), - now: time.Now, + servers: deps.Servers, + users: deps.Users, + projectMembers: deps.ProjectMembers, + projectDirectory: deps.ProjectDirectory, + projectLifecycle: deps.ProjectLifecycle, + projectResources: deps.ProjectResources, + projectBindings: deps.ProjectBindings, + projectDomains: deps.ProjectDomains, + sshKeys: deps.SSHKeys, + deploymentFactory: deps.DeploymentFactory, + issueCertificate: deps.IssueCertificate, + issueSetupCertificate: deps.IssueSetupCertificate, + rollbackSetupCertificate: deps.RollbackSetupCertificate, + importCertificate: deps.ImportCertificate, + deployCertificate: deps.DeployCertificate, + restoreVersion: deps.RestoreCertificateVersion, + validateCertificateWorkflow: deps.ValidateCertificate, + runRenewal: deps.RunRenewal, + updateRenewalPolicy: deps.UpdateRenewalPolicy, + currentCertificate: deps.CurrentCertificate, + resetStatePath: deps.ResetStatePath, + audit: deps.Audit, + tasks: deps.Tasks, + readinessCheck: deps.ReadinessCheck, + aliyunChecker: deps.AliyunChecker, + taskCancels: make(map[string]context.CancelFunc), + resetLimiter: newResetRateLimiter(), + authSessions: make(map[string]authSession), + loginAttempts: make(map[string]loginAttempt), + loginSources: make(map[string]loginSource), + loginPending: make(map[string]int), + now: time.Now, } router.Use(server.requestMiddleware()) server.registerRoutes()