diff --git a/backend/app/dto/request/website_ssl.go b/backend/app/dto/request/website_ssl.go index bcb8b4a28..f4d125104 100644 --- a/backend/app/dto/request/website_ssl.go +++ b/backend/app/dto/request/website_ssl.go @@ -12,6 +12,7 @@ type WebsiteSSLCreate struct { Provider string `json:"provider" validate:"required"` AcmeAccountID uint `json:"acmeAccountId" validate:"required"` DnsAccountID uint `json:"dnsAccountId"` + AutoRenew bool `json:"autoRenew" validate:"required"` } type WebsiteDNSReq struct { diff --git a/backend/app/model/website_ssl.go b/backend/app/model/website_ssl.go index 92d48aae8..0d13048da 100644 --- a/backend/app/model/website_ssl.go +++ b/backend/app/model/website_ssl.go @@ -4,19 +4,21 @@ import "time" 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"` - AcmeAccount WebsiteAcmeAccount `json:"acmeAccount"` - ExpireDate time.Time `json:"expireDate"` - StartDate time.Time `json:"startDate"` + 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"` + AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"` + ExpireDate time.Time `json:"expireDate"` + StartDate time.Time `json:"startDate"` + + AcmeAccount WebsiteAcmeAccount `json:"acmeAccount"` } func (w WebsiteSSL) TableName() string { diff --git a/backend/app/service/nginx_utils.go b/backend/app/service/nginx_utils.go index e3306c523..bb0f9023b 100644 --- a/backend/app/service/nginx_utils.go +++ b/backend/app/service/nginx_utils.go @@ -205,7 +205,6 @@ func nginxCheckAndReload(oldContent string, filePath string, containerName strin _ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), 0644) return err } - if err := opNginx(containerName, constant.NginxReload); err != nil { _ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), 0644) return err diff --git a/backend/app/service/website_ssl.go b/backend/app/service/website_ssl.go index 26262bdfd..819ff2d0f 100644 --- a/backend/app/service/website_ssl.go +++ b/backend/app/service/website_ssl.go @@ -9,6 +9,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/ssl" "path" "strings" @@ -17,6 +18,21 @@ import ( type WebsiteSSLService struct { } +type IWebsiteSSLService interface { + Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error) + GetSSL(id uint) (*response.WebsiteSSLDTO, error) + Search() ([]response.WebsiteSSLDTO, error) + Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) + Renew(sslId uint) error + GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) + GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error) + Delete(id uint) error +} + +func NewIWebsiteSSLService() IWebsiteSSLService { + return &WebsiteSSLService{} +} + func (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error) { total, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc")) if err != nil { @@ -151,8 +167,6 @@ func (w WebsiteSSLService) Renew(sslId uint) error { if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppNginx, appInstall.Name, "root")); err != nil { return err } - case constant.DnsManual: - } resource, err := client.RenewSSL(websiteSSL.CertURL) @@ -172,7 +186,26 @@ func (w WebsiteSSLService) Renew(sslId uint) error { websiteSSL.Type = cert.Issuer.CommonName websiteSSL.Organization = cert.Issuer.Organization[0] - return websiteSSLRepo.Save(websiteSSL) + if err := websiteSSLRepo.Save(websiteSSL); err != nil { + return err + } + + websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(sslId)) + for _, website := range websites { + if err := createPemFile(website, websiteSSL); err != nil { + global.LOG.Errorf("create website [%s] ssl file failed! err:%s", website.PrimaryDomain, err.Error()) + } + } + if len(websites) > 0 { + nginxInstall, err := getAppInstallByKey(constant.AppNginx) + if err != nil { + return err + } + if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { + return buserr.New(constant.ErrSSLApply) + } + } + return nil } func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) { diff --git a/backend/constant/errs.go b/backend/constant/errs.go index 450aeb9c4..98d597d3c 100644 --- a/backend/constant/errs.go +++ b/backend/constant/errs.go @@ -66,4 +66,5 @@ var ( var ( ErrSSLCannotDelete = "ErrSSLCannotDelete" ErrAccountCannotDelete = "ErrAccountCannotDelete" + ErrSSLApply = "ErrSSLApply" ) diff --git a/backend/cron/cron.go b/backend/cron/cron.go index 6a85d485e..048355e2a 100644 --- a/backend/cron/cron.go +++ b/backend/cron/cron.go @@ -22,6 +22,10 @@ func Run() { if err != nil { global.LOG.Errorf("can not add website corn job: %s", err.Error()) } + _, err = Cron.AddJob("@daily", job.NewSSLJob()) + if err != nil { + global.LOG.Errorf("can not add ssl corn job: %s", err.Error()) + } Cron.Start() global.Cron = Cron diff --git a/backend/cron/job/ssl.go b/backend/cron/job/ssl.go new file mode 100644 index 000000000..8792cbc06 --- /dev/null +++ b/backend/cron/job/ssl.go @@ -0,0 +1,36 @@ +package job + +import ( + "github.com/1Panel-dev/1Panel/backend/app/repo" + "github.com/1Panel-dev/1Panel/backend/app/service" + "github.com/1Panel-dev/1Panel/backend/global" + "time" +) + +type ssl struct { +} + +func NewSSLJob() *ssl { + return &ssl{} +} + +func (ssl *ssl) Run() { + sslRepo := repo.NewISSLRepo() + sslService := service.NewIWebsiteSSLService() + sslList, _ := sslRepo.List() + global.LOG.Info("ssl renew cron job start...") + now := time.Now() + for _, s := range sslList { + if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" { + continue + } + sum := s.ExpireDate.Sub(now) + if sum.Hours() < 168 { + if err := sslService.Renew(s.ID); err != nil { + global.LOG.Errorf("renew doamin [%s] ssl failed err:%s", s.PrimaryDomain, err.Error()) + } + } + } + + global.LOG.Info("ssl renew cron job end...") +} diff --git a/backend/cron/job/website.go b/backend/cron/job/website.go index fa3c751f7..9a09a50c1 100644 --- a/backend/cron/job/website.go +++ b/backend/cron/job/website.go @@ -18,7 +18,7 @@ func NewWebsiteJob() *website { func (w *website) Run() { websites, _ := repo.NewIWebsiteRepo().List() - global.LOG.Info("website cron job start....") + global.LOG.Info("website cron job start...") now := time.Now() if len(websites) > 0 { neverExpireDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate) @@ -36,7 +36,7 @@ func (w *website) Run() { } wg.Wait() } - global.LOG.Info("website cron job end") + global.LOG.Info("website cron job end...") } func stopWebsite(websiteId uint, wg *sync.WaitGroup) { diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 5b438ff5b..d4d339291 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -34,4 +34,5 @@ ErrAppDelete: 'Other Website use this App' #ssl ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed" -ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted" \ No newline at end of file +ErrAccountCannotDelete: "The certificate associated with the account cannot be deleted" +ErrSSLApply: "The certificate continues to be signed successfully, but openresty reload fails, please check the configuration!" \ No newline at end of file diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index b820ed461..8f7c20c43 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -33,4 +33,5 @@ ErrAppDelete: '其他网站使用此应用,不能删除' #ssl ErrSSLCannotDelete: "证书正在被网站使用,无法删除" -ErrAccountCannotDelete: "账号关联证书,无法删除" \ No newline at end of file +ErrAccountCannotDelete: "账号关联证书,无法删除" +ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!" \ No newline at end of file diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index 429d7e448..57099181c 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -12,6 +12,7 @@ export namespace Website { webSiteGroupId: number; otherDomains: string; defaultServer: boolean; + autoRenew: boolean; appinstall?: NewAppInstall; webSiteSSL: SSL; } diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 48b7532ed..88a9c1316 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -999,6 +999,9 @@ export default { startDate: '生效时间', organization: '签发机构', renewConfirm: '是否确定续签?', + autoRenew: '自动续签', + autoRenewHelper: '距离到期时间7天自动续签', + renewSuccess: '续签成功', }, firewall: { ccDeny: 'CC 防护', diff --git a/frontend/src/views/website/ssl/create/index.vue b/frontend/src/views/website/ssl/create/index.vue index 6748459b6..0cb01aa00 100644 --- a/frontend/src/views/website/ssl/create/index.vue +++ b/frontend/src/views/website/ssl/create/index.vue @@ -4,7 +4,7 @@ :title="$t('commons.button.create')" :destroy-on-close="true" :close-on-click-modal="false" - width="60%" + width="50%" :before-close="handleClose" > + + +