diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go
index e6a04bcd..f1200094 100644
--- a/internal/applicant/applicant.go
+++ b/internal/applicant/applicant.go
@@ -26,13 +26,14 @@ import (
)
type ApplyResult struct {
+ CSR string
FullChainCertificate string
IssuerCertificate string
PrivateKey string
ACMEAccountUrl string
ACMECertUrl string
ACMECertStableUrl string
- CSR string
+ ARIReplaced bool
}
type Applicant interface {
@@ -109,7 +110,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
certRepo := repository.NewCertificateRepository()
lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id)
- if lastCertificate != nil {
+ if lastCertificate != nil && !lastCertificate.ACMERenewed {
newCertSan := slices.Clone(options.Domains)
oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";")
slices.Sort(newCertSan)
@@ -119,8 +120,8 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate))
if lastCertX509 != nil {
replacedARICertId, _ := certificate.MakeARICertID(lastCertX509)
- options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl
- options.ReplacedARICert = replacedARICertId
+ options.ARIReplaceAcct = lastCertificate.ACMEAccountUrl
+ options.ARIReplaceCert = replacedARICertId
}
}
}
@@ -235,22 +236,24 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt
Domains: options.Domains,
Bundle: true,
}
- if options.ReplacedARIAcct == user.Registration.URI {
- certRequest.ReplacesCertID = options.ReplacedARICert
+ if options.ARIReplaceAcct == user.Registration.URI {
+ certRequest.ReplacesCertID = options.ARIReplaceCert
}
+
certResource, err := client.Certificate.Obtain(certRequest)
if err != nil {
return nil, err
}
return &ApplyResult{
+ CSR: strings.TrimSpace(string(certResource.CSR)),
FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)),
IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)),
PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)),
ACMEAccountUrl: user.Registration.URI,
ACMECertUrl: certResource.CertURL,
ACMECertStableUrl: certResource.CertStableURL,
- CSR: strings.TrimSpace(string(certResource.CSR)),
+ ARIReplaced: certRequest.ReplacesCertID != "",
}, nil
}
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index de47ae18..175531ae 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -57,8 +57,8 @@ type applicantProviderOptions struct {
DnsPropagationTimeout int32
DnsTTL int32
DisableFollowCNAME bool
- ReplacedARIAcct string
- ReplacedARICert string
+ ARIReplaceAcct string
+ ARIReplaceCert string
}
func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) {
diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go
index 710ce268..b2b48fcd 100644
--- a/internal/domain/certificate.go
+++ b/internal/domain/certificate.go
@@ -28,6 +28,7 @@ type Certificate struct {
ACMEAccountUrl string `json:"acmeAccountUrl" db:"acmeAccountUrl"`
ACMECertUrl string `json:"acmeCertUrl" db:"acmeCertUrl"`
ACMECertStableUrl string `json:"acmeCertStableUrl" db:"acmeCertStableUrl"`
+ ACMERenewed bool `json:"acmeRenewed" db:"acmeRenewed"`
WorkflowId string `json:"workflowId" db:"workflowId"`
WorkflowNodeId string `json:"workflowNodeId" db:"workflowNodeId"`
WorkflowRunId string `json:"workflowRunId" db:"workflowRunId"`
diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go
index 690e5242..3a92bb7c 100644
--- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go
+++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go
@@ -55,9 +55,10 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
- ApiUrl: config.ApiUrl,
- ApiVersion: config.ApiVersion,
- ApiKey: config.ApiKey,
+ ApiUrl: config.ApiUrl,
+ ApiVersion: config.ApiVersion,
+ ApiKey: config.ApiKey,
+ AllowInsecureConnections: config.AllowInsecureConnections,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go
index 63900125..2a28c82c 100644
--- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go
+++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go
@@ -2,6 +2,7 @@ package onepanelssl
import (
"context"
+ "crypto/tls"
"errors"
"fmt"
"log/slog"
@@ -20,6 +21,8 @@ type UploaderConfig struct {
ApiVersion string `json:"apiVersion"`
// 1Panel 接口密钥。
ApiKey string `json:"apiKey"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type UploaderProvider struct {
@@ -35,7 +38,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
panic("config is nil")
}
- client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey)
+ client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
@@ -132,7 +135,7 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string,
return nil, nil
}
-func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) {
+func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
if _, err := url.Parse(apiUrl); err != nil {
return nil, errors.New("invalid 1panel api url")
}
@@ -146,5 +149,9 @@ func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, er
}
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
return client, nil
}
diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go
index 95bfd713..290d5f9f 100644
--- a/internal/repository/certificate.go
+++ b/internal/repository/certificate.go
@@ -77,6 +77,25 @@ func (r *CertificateRepository) GetByWorkflowNodeId(ctx context.Context, workflo
return r.castRecordToModel(records[0])
}
+func (r *CertificateRepository) GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error) {
+ records, err := app.GetApp().FindRecordsByFilter(
+ domain.CollectionNameCertificate,
+ "workflowRunId={:workflowRunId} && deleted=null",
+ "-created",
+ 1, 0,
+ dbx.Params{"workflowRunId": workflowRunId},
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ if len(records) == 0 {
+ return nil, domain.ErrRecordNotFound
+ }
+
+ return r.castRecordToModel(records[0])
+}
+
func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error) {
collection, err := app.GetApp().FindCollectionByNameOrId(domain.CollectionNameCertificate)
if err != nil {
@@ -109,6 +128,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce
record.Set("acmeAccountUrl", certificate.ACMEAccountUrl)
record.Set("acmeCertUrl", certificate.ACMECertUrl)
record.Set("acmeCertStableUrl", certificate.ACMECertStableUrl)
+ record.Set("acmeRenewed", certificate.ACMERenewed)
record.Set("workflowId", certificate.WorkflowId)
record.Set("workflowRunId", certificate.WorkflowRunId)
record.Set("workflowNodeId", certificate.WorkflowNodeId)
@@ -170,6 +190,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain.
ACMEAccountUrl: record.GetString("acmeAccountUrl"),
ACMECertUrl: record.GetString("acmeCertUrl"),
ACMECertStableUrl: record.GetString("acmeCertStableUrl"),
+ ACMERenewed: record.GetBool("acmeRenewed"),
WorkflowId: record.GetString("workflowId"),
WorkflowRunId: record.GetString("workflowRunId"),
WorkflowNodeId: record.GetString("workflowNodeId"),
diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go
index ff8c573d..d38ece89 100644
--- a/internal/workflow/node-processor/apply_node.go
+++ b/internal/workflow/node-processor/apply_node.go
@@ -96,6 +96,15 @@ func (n *applyNode) Process(ctx context.Context) error {
return err
}
+ // 保存 ARI 记录
+ if applyResult.ARIReplaced {
+ lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId)
+ if lastCertificate != nil {
+ lastCertificate.ACMERenewed = true
+ n.certRepo.Save(ctx, lastCertificate)
+ }
+ }
+
n.logger.Info("apply completed")
return nil
@@ -134,7 +143,7 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo
return false, "the configuration item 'KeyAlgorithm' changed"
}
- lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id)
+ lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId)
if lastCertificate != nil {
renewalInterval := time.Duration(currentNodeConfig.SkipBeforeExpiryDays) * time.Hour * 24
expirationTime := time.Until(lastCertificate.ExpireAt)
diff --git a/internal/workflow/node-processor/processor.go b/internal/workflow/node-processor/processor.go
index 4523b13a..f98aebae 100644
--- a/internal/workflow/node-processor/processor.go
+++ b/internal/workflow/node-processor/processor.go
@@ -34,6 +34,8 @@ func (n *nodeProcessor) SetLogger(logger *slog.Logger) {
type certificateRepository interface {
GetByWorkflowNodeId(ctx context.Context, workflowNodeId string) (*domain.Certificate, error)
+ GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error)
+ Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error)
}
type workflowOutputRepository interface {
diff --git a/internal/workflow/node-processor/upload_node.go b/internal/workflow/node-processor/upload_node.go
index 2da19eed..a1878a41 100644
--- a/internal/workflow/node-processor/upload_node.go
+++ b/internal/workflow/node-processor/upload_node.go
@@ -83,7 +83,7 @@ func (n *uploadNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workfl
return false, "the configuration item 'PrivateKey' changed"
}
- lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id)
+ lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId)
if lastCertificate != nil {
return true, "the certificate has already been uploaded"
}
diff --git a/migrations/1748228400_upgrade.go b/migrations/1748228400_upgrade.go
new file mode 100644
index 00000000..b6f954d2
--- /dev/null
+++ b/migrations/1748228400_upgrade.go
@@ -0,0 +1,39 @@
+package migrations
+
+import (
+ "github.com/pocketbase/pocketbase/core"
+ m "github.com/pocketbase/pocketbase/migrations"
+)
+
+func init() {
+ m.Register(func(app core.App) error {
+ // update collection `certificate`
+ {
+ collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np")
+ if err != nil {
+ return err
+ }
+
+ // add field
+ if err := collection.Fields.AddMarshaledJSONAt(14, []byte(`{
+ "hidden": false,
+ "id": "bool810050391",
+ "name": "acmeRenewed",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "bool"
+ }`)); err != nil {
+ return err
+ }
+
+ if err := app.Save(collection); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }, func(app core.App) error {
+ return nil
+ })
+}
diff --git a/ui/src/components/access/AccessEditModal.tsx b/ui/src/components/access/AccessEditModal.tsx
index 8d2d5204..a20d50b9 100644
--- a/ui/src/components/access/AccessEditModal.tsx
+++ b/ui/src/components/access/AccessEditModal.tsx
@@ -100,6 +100,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ..
}}
afterClose={() => setOpen(false)}
cancelButtonProps={{ disabled: formPending }}
+ cancelText={t("common.button.cancel")}
closable
confirmLoading={formPending}
destroyOnHidden
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index 80237287..e0602370 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -109,7 +109,7 @@
"workflow_node.deploy.form.certificate.placeholder": "Please select certificate",
"workflow_node.deploy.form.certificate.tooltip": "The certificate to be deployed comes from the previous nodes of application or upload.",
"workflow_node.deploy.form.params_config.label": "Parameter settings",
- "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart after deployment",
+ "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart 1Panel after deployment",
"workflow_node.deploy.form.1panel_site_resource_type.label": "Resource type",
"workflow_node.deploy.form.1panel_site_resource_type.placeholder": "Please select resource type",
"workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "Website",
@@ -331,7 +331,7 @@
"workflow_node.deploy.form.baishan_cdn_certificate_id.label": "Baishan Cloud CDN certificate ID (Optional)",
"workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "Please enter Baishan Cloud CDN certificate ID",
"workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "For more information, see https://cdnx.console.baishan.com/#/cdn/cert",
- "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart after deployment",
+ "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart aaPanel after deployment",
"workflow_node.deploy.form.baotapanel_site_type.label": "aaPanel site type",
"workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select aaPanel site type",
"workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP sites",
@@ -530,7 +530,7 @@
"workflow_node.deploy.form.netlify_site_id.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#get-site",
"workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE cluster node name",
"workflow_node.deploy.form.proxmoxve_node_name.placeholder": "Please enter Proxmox VE cluster node name",
- "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart after deployment",
+ "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart Proxmox VE after deployment",
"workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain",
"workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name",
"workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see https://portal.qiniu.com/cdn",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index faf40816..2dbcb3ca 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -108,7 +108,7 @@
"workflow_node.deploy.form.certificate.placeholder": "请选择待部署证书",
"workflow_node.deploy.form.certificate.tooltip": "待部署证书来自之前的申请或上传节点。如果选项为空请先确保前序节点配置正确。",
"workflow_node.deploy.form.params_config.label": "参数设置",
- "workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启宝塔面板服务",
+ "workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启 1Panel 服务",
"workflow_node.deploy.form.1panel_site_resource_type.label": "证书部署方式",
"workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "替换指定网站的证书",
@@ -330,7 +330,7 @@
"workflow_node.deploy.form.baishan_cdn_certificate_id.label": "白山云 CDN 原证书 ID(可选)",
"workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "请输入白山云 CDN 原证书 ID",
"workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdnx.console.baishan.com/#/cdn/cert
不填写时,将上传新证书;否则,将替换原证书。",
- "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启 1Panel 服务",
+ "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启宝塔面板服务",
"workflow_node.deploy.form.baotapanel_site_type.label": "宝塔面板网站类型",
"workflow_node.deploy.form.baotapanel_site_type.placeholder": "请选择宝塔面板网站类型",
"workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP",