From 43ebf6eef9bc595149a9efd7cae68469613326a9 Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Tue, 7 May 2024 18:01:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=81=E4=B9=A6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=20(#4901)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/dto/request/website_ssl.go | 20 +++-- backend/app/model/website_ssl.go | 30 +++---- backend/app/repo/website_ssl.go | 11 ++- backend/app/service/website_ca.go | 1 + backend/app/service/website_ssl.go | 48 +++++++++++- backend/utils/ssl/client.go | 41 +++++----- frontend/src/api/interface/website.ts | 14 +++- .../src/views/website/ssl/create/index.vue | 78 +++++++++++++++---- frontend/src/views/website/ssl/index.vue | 29 +++++-- 9 files changed, 202 insertions(+), 70 deletions(-) diff --git a/backend/app/dto/request/website_ssl.go b/backend/app/dto/request/website_ssl.go index 1bb9ef682..b161d323d 100644 --- a/backend/app/dto/request/website_ssl.go +++ b/backend/app/dto/request/website_ssl.go @@ -32,8 +32,9 @@ type WebsiteSSLRenew struct { } type WebsiteSSLApply struct { - ID uint `json:"ID" validate:"required"` - SkipDNSCheck bool `json:"SkipDNSCheck"` + ID uint `json:"ID" validate:"required"` + SkipDNSCheck bool `json:"skipDNSCheck"` + Nameservers []string `json:"nameservers"` } type WebsiteAcmeAccountCreate struct { @@ -66,9 +67,18 @@ type WebsiteBatchDelReq struct { } type WebsiteSSLUpdate struct { - ID uint `json:"id" validate:"required"` - AutoRenew bool `json:"autoRenew"` - Description string `json:"description"` + ID uint `json:"id" validate:"required"` + AutoRenew bool `json:"autoRenew"` + Description string `json:"description"` + PrimaryDomain string `json:"primaryDomain" validate:"required"` + OtherDomains string `json:"otherDomains"` + Provider string `json:"provider" validate:"required"` + AcmeAccountID uint `json:"acmeAccountId" validate:"required"` + DnsAccountID uint `json:"dnsAccountId"` + KeyType string `json:"keyType"` + Apply bool `json:"apply"` + PushDir bool `json:"pushDir"` + Dir string `json:"dir"` } type WebsiteSSLUpload struct { diff --git a/backend/app/model/website_ssl.go b/backend/app/model/website_ssl.go index aef16ca0f..b971f8603 100644 --- a/backend/app/model/website_ssl.go +++ b/backend/app/model/website_ssl.go @@ -9,24 +9,24 @@ import ( type WebsiteSSL struct { BaseModel - PrimaryDomain string `gorm:"type:varchar(256);not null" json:"primaryDomain"` - PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"` - Pem string `gorm:"type:longtext;not null" json:"pem"` - Domains string `gorm:"type:varchar(256);not null" json:"domains"` - CertURL string `gorm:"type:varchar(256);not null" json:"certURL"` - Type string `gorm:"type:varchar(64);not null" json:"type"` - Provider string `gorm:"type:varchar(64);not null" json:"provider"` - Organization string `gorm:"type:varchar(64);not null" json:"organization"` - DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"` - AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"` - CaID uint `gorm:"type:integer;not null;default:0" json:"caId"` - AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"` + PrimaryDomain string `json:"primaryDomain"` + PrivateKey string `json:"privateKey"` + Pem string `json:"pem"` + Domains string `json:"domains"` + CertURL string `json:"certURL"` + Type string `json:"type"` + Provider string `json:"provider"` + Organization string `json:"organization"` + DnsAccountID uint `json:"dnsAccountId"` + AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" ` + CaID uint `json:"caId"` + AutoRenew bool `json:"autoRenew"` ExpireDate time.Time `json:"expireDate"` StartDate time.Time `json:"startDate"` - Status string `gorm:"not null;default:ready" json:"status"` + Status string `json:"status"` Message string `json:"message"` - KeyType string `gorm:"not null;default:2048" json:"keyType"` - PushDir bool `gorm:"not null;default:0" json:"pushDir"` + KeyType string `json:"keyType"` + PushDir bool `json:"pushDir"` Dir string `json:"dir"` Description string `json:"description"` diff --git a/backend/app/repo/website_ssl.go b/backend/app/repo/website_ssl.go index 7d875a140..bac38543b 100644 --- a/backend/app/repo/website_ssl.go +++ b/backend/app/repo/website_ssl.go @@ -21,6 +21,7 @@ type ISSLRepo interface { Create(ctx context.Context, ssl *model.WebsiteSSL) error Save(ssl *model.WebsiteSSL) error DeleteBy(opts ...DBOption) error + SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error } type WebsiteSSLRepo struct { @@ -82,7 +83,15 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error } func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error { - return getDb().Save(&ssl).Error + return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{ + ID: ssl.ID, + }}).Save(&ssl).Error +} + +func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error { + return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{ + ID: ssl.ID, + }}).Updates(params).Error } func (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error { diff --git a/backend/app/service/website_ca.go b/backend/app/service/website_ca.go index 607f6653a..27142bc6e 100644 --- a/backend/app/service/website_ca.go +++ b/backend/app/service/website_ca.go @@ -346,6 +346,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website websiteSSL.StartDate = cert.NotBefore websiteSSL.Type = cert.Issuer.CommonName websiteSSL.Organization = rootCsr.Subject.Organization[0] + websiteSSL.Status = constant.SSLReady if req.Renew { if err := websiteSSLRepo.Save(websiteSSL); err != nil { diff --git a/backend/app/service/website_ssl.go b/backend/app/service/website_ssl.go index 596b802ed..164c36058 100644 --- a/backend/app/service/website_ssl.go +++ b/backend/app/service/website_ssl.go @@ -191,7 +191,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { if err != nil { return err } - if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck); err != nil { + if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck, apply.Nameservers); err != nil { return err } case constant.Http: @@ -368,9 +368,49 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error { if err != nil { return err } - websiteSSL.AutoRenew = update.AutoRenew - websiteSSL.Description = update.Description - return websiteSSLRepo.Save(websiteSSL) + updateParams := make(map[string]interface{}) + updateParams["primary_domain"] = update.PrimaryDomain + updateParams["description"] = update.Description + updateParams["provider"] = update.Provider + updateParams["key_type"] = update.KeyType + updateParams["push_dir"] = update.PushDir + + acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID)) + if err != nil { + return err + } + updateParams["acme_account_id"] = acmeAccount.ID + + if update.PushDir { + if !files.NewFileOp().Stat(update.Dir) { + return buserr.New(constant.ErrLinkPathNotFound) + } + updateParams["dir"] = update.Dir + } + var domains []string + if update.OtherDomains != "" { + otherDomainArray := strings.Split(update.OtherDomains, "\n") + for _, domain := range otherDomainArray { + if !common.IsValidDomain(domain) { + return buserr.WithName("ErrDomainFormat", domain) + } + domains = append(domains, domain) + } + } + updateParams["domains"] = strings.Join(domains, ",") + if update.Provider == constant.DNSAccount || update.Provider == constant.Http { + updateParams["auto_renew"] = update.AutoRenew + } else { + updateParams["auto_renew"] = false + } + if update.Provider == constant.DNSAccount { + dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(update.DnsAccountID)) + if err != nil { + return err + } + updateParams["dns_account_id"] = dnsAccount.ID + } + return websiteSSLRepo.SaveByMap(websiteSSL, updateParams) } func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error { diff --git a/backend/utils/ssl/client.go b/backend/utils/ssl/client.go index f624d6c1e..fcf1b7efa 100644 --- a/backend/utils/ssl/client.go +++ b/backend/utils/ssl/client.go @@ -84,7 +84,7 @@ type DNSParam struct { SecretID string `json:"secretID"` } -func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) error { +func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool, nameservers []string) error { var ( param DNSParam p challenge.Provider @@ -99,15 +99,15 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) e case DnsPod: dnsPodConfig := dnspod.NewDefaultConfig() dnsPodConfig.LoginToken = param.ID + "," + param.Token - dnsPodConfig.PropagationTimeout = 60 * time.Minute - dnsPodConfig.PollingInterval = 5 * time.Second + dnsPodConfig.PropagationTimeout = 15 * time.Minute + dnsPodConfig.PollingInterval = 10 * time.Second dnsPodConfig.TTL = 3600 p, err = dnspod.NewDNSProviderConfig(dnsPodConfig) case AliYun: alidnsConfig := alidns.NewDefaultConfig() alidnsConfig.SecretKey = param.SecretKey alidnsConfig.APIKey = param.AccessKey - alidnsConfig.PropagationTimeout = 60 * time.Minute + alidnsConfig.PropagationTimeout = 15 * time.Minute alidnsConfig.PollingInterval = 5 * time.Second alidnsConfig.TTL = 3600 p, err = alidns.NewDNSProviderConfig(alidnsConfig) @@ -115,58 +115,61 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) e cloudflareConfig := cloudflare.NewDefaultConfig() cloudflareConfig.AuthEmail = param.Email cloudflareConfig.AuthToken = param.APIkey - cloudflareConfig.PropagationTimeout = 60 * time.Minute - cloudflareConfig.PollingInterval = 5 * time.Second + cloudflareConfig.PropagationTimeout = 15 * time.Minute + cloudflareConfig.PollingInterval = 10 * time.Second cloudflareConfig.TTL = 3600 p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig) case NameCheap: namecheapConfig := namecheap.NewDefaultConfig() namecheapConfig.APIKey = param.APIkey namecheapConfig.APIUser = param.APIUser - namecheapConfig.PropagationTimeout = 60 * time.Minute + namecheapConfig.PropagationTimeout = 15 * time.Minute namecheapConfig.PollingInterval = 5 * time.Second namecheapConfig.TTL = 3600 p, err = namecheap.NewDNSProviderConfig(namecheapConfig) case NameSilo: nameSiloConfig := namesilo.NewDefaultConfig() nameSiloConfig.APIKey = param.APIkey - nameSiloConfig.PropagationTimeout = 60 * time.Minute - nameSiloConfig.PollingInterval = 5 * time.Second + nameSiloConfig.PropagationTimeout = 15 * time.Minute + nameSiloConfig.PollingInterval = 10 * time.Second nameSiloConfig.TTL = 3600 p, err = namesilo.NewDNSProviderConfig(nameSiloConfig) case Godaddy: godaddyConfig := godaddy.NewDefaultConfig() godaddyConfig.APIKey = param.APIkey godaddyConfig.APISecret = param.APISecret - godaddyConfig.PropagationTimeout = 60 * time.Minute - godaddyConfig.PollingInterval = 5 * time.Second + godaddyConfig.PropagationTimeout = 15 * time.Minute + godaddyConfig.PollingInterval = 10 * time.Second godaddyConfig.TTL = 3600 p, err = godaddy.NewDNSProviderConfig(godaddyConfig) case NameCom: nameComConfig := namedotcom.NewDefaultConfig() nameComConfig.APIToken = param.Token nameComConfig.Username = param.APIUser - nameComConfig.PropagationTimeout = 30 * time.Minute - nameComConfig.PollingInterval = 30 * time.Second + nameComConfig.PropagationTimeout = 15 * time.Minute + nameComConfig.PollingInterval = 10 * time.Second nameComConfig.TTL = 3600 p, err = namedotcom.NewDNSProviderConfig(nameComConfig) case TencentCloud: tencentCloudConfig := tencentcloud.NewDefaultConfig() tencentCloudConfig.SecretID = param.SecretID tencentCloudConfig.SecretKey = param.SecretKey - tencentCloudConfig.PropagationTimeout = 30 * time.Minute - tencentCloudConfig.PollingInterval = 30 * time.Second + tencentCloudConfig.PropagationTimeout = 15 * time.Minute + tencentCloudConfig.PollingInterval = 10 * time.Second tencentCloudConfig.TTL = 3600 p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig) } if err != nil { return err } - if skipDNSCheck { - return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute), dns01.DisableCompletePropagationRequirement()) - } - return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute)) + return c.Client.Challenge.SetDNS01Provider(p, + dns01.CondOption(len(nameservers) > 0, + dns01.AddRecursiveNameservers(nameservers)), + dns01.CondOption(skipDNSCheck, + dns01.DisableCompletePropagationRequirement()), + dns01.AddDNSTimeout(10*time.Minute), + ) } func (c *AcmeClient) UseManualDns() error { diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index d18c4e947..a6a349760 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -165,10 +165,14 @@ export namespace Website { provider: string; websites?: Website.Website[]; autoRenew: boolean; - acmeAccountId?: number; + acmeAccountId: number; status: string; domains: string; description: string; + dnsAccountId?: number; + pushDir: boolean; + dir: string; + keyType: string; } export interface SSLDTO extends SSL { @@ -198,6 +202,14 @@ export namespace Website { id: number; autoRenew: boolean; description: string; + primaryDomain: string; + otherDomains: string; + acmeAccountId: number; + provider: string; + dnsAccountId?: number; + keyType: string; + pushDir: boolean; + dir: string; } export interface AcmeAccount extends CommonModel { diff --git a/frontend/src/views/website/ssl/create/index.vue b/frontend/src/views/website/ssl/create/index.vue index 8753ae6ee..9519cce85 100644 --- a/frontend/src/views/website/ssl/create/index.vue +++ b/frontend/src/views/website/ssl/create/index.vue @@ -136,7 +136,7 @@