1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 自签证书增加续签功能 (#3079)

This commit is contained in:
zhengkunwang 2023-11-27 18:36:10 +08:00 committed by GitHub
parent 807a5071a7
commit e41661e8d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 300 additions and 406 deletions

View File

@ -116,3 +116,29 @@ func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {
} }
helper.SuccessWithOutData(c) helper.SuccessWithOutData(c)
} }
// @Tags Website CA
// @Summary Obtain SSL
// @Description 续签 SSL 证书
// @Accept json
// @Param request body request.WebsiteCAObtain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ca/obtain [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"website_cas","output_column":"name","output_value":"name"}],"formatZH":"自签 SSL 证书 [name]","formatEN":"Obtain SSL [name]"}
func (b *BaseApi) RenewWebsiteCA(c *gin.Context) {
var req request.WebsiteCARenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteCAService.ObtainSSL(request.WebsiteCAObtain{
SSLID: req.SSLID,
Renew: true,
Unit: "year",
Time: 1,
}); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}

View File

@ -65,27 +65,6 @@ func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
helper.SuccessWithData(c, res) helper.SuccessWithData(c, res)
} }
// @Tags Website SSL
// @Summary Reset website ssl
// @Description 重置网站 ssl
// @Accept json
// @Param request body request.WebsiteSSLRenew true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/renew [post]
// @x-panel-log {"bodyKeys":["SSLId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"SSLId","isList":false,"db":"website_ssls","output_column":"primary_domain","output_value":"domain"}],"formatZH":"重置 ssl [domain]","formatEN":"Renew ssl [domain]"}
func (b *BaseApi) RenewWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLRenew
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.Renew(req.SSLID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website SSL // @Tags Website SSL
// @Summary Apply ssl // @Summary Apply ssl
// @Description 申请证书 // @Description 申请证书

View File

@ -92,11 +92,18 @@ type WebsiteCACreate struct {
} }
type WebsiteCAObtain struct { type WebsiteCAObtain struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
Domains string `json:"domains" validate:"required"` Domains string `json:"domains" validate:"required"`
KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"` KeyType string `json:"keyType" validate:"required,oneof=P256 P384 2048 3072 4096 8192"`
Time int `json:"time" validate:"required"` Time int `json:"time" validate:"required"`
Unit string `json:"unit" validate:"required"` Unit string `json:"unit" validate:"required"`
PushDir bool `json:"pushDir"` PushDir bool `json:"pushDir"`
Dir string `json:"dir"` Dir string `json:"dir"`
AutoRenew bool `json:"autoRenew"`
Renew bool `json:"renew"`
SSLID uint `json:"sslID"`
}
type WebsiteCARenew struct {
SSLID uint `json:"SSLID" validate:"required"`
} }

View File

@ -19,6 +19,7 @@ type WebsiteSSL struct {
Organization string `gorm:"type:varchar(64);not null" json:"organization"` Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"` DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"` 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"` AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"` ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"` StartDate time.Time `json:"startDate"`

View File

@ -14,11 +14,12 @@ type ISSLRepo interface {
WithByAlias(alias string) DBOption WithByAlias(alias string) DBOption
WithByAcmeAccountId(acmeAccountId uint) DBOption WithByAcmeAccountId(acmeAccountId uint) DBOption
WithByDnsAccountId(dnsAccountId uint) DBOption WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (model.WebsiteSSL, error) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error) List(opts ...DBOption) ([]model.WebsiteSSL, error)
Create(ctx context.Context, ssl *model.WebsiteSSL) error Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl model.WebsiteSSL) error Save(ssl *model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error DeleteBy(opts ...DBOption) error
} }
@ -43,6 +44,12 @@ func (w WebsiteSSLRepo) WithByDnsAccountId(dnsAccountId uint) DBOption {
} }
} }
func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) { func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{}) db := getDb(opts...).Model(&model.WebsiteSSL{})
@ -52,8 +59,8 @@ func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.W
return count, sslList, err return count, sslList, err
} }
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebsiteSSL, error) { func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {
var website model.WebsiteSSL var website *model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{}) db := getDb(opts...).Model(&model.WebsiteSSL{})
if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil { if err := db.Preload("AcmeAccount").Preload("DnsAccount").First(&website).Error; err != nil {
return website, err return website, err
@ -74,7 +81,7 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
return getTx(ctx).Create(ssl).Error return getTx(ctx).Create(ssl).Error
} }
func (w WebsiteSSLRepo) Save(ssl model.WebsiteSSL) error { func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
return getDb().Save(&ssl).Error return getDb().Save(&ssl).Error
} }

View File

@ -619,7 +619,7 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
if err != nil { if err != nil {
return response.WebsiteHTTPS{}, err return response.WebsiteHTTPS{}, err
} }
res.SSL = websiteSSL res.SSL = *websiteSSL
res.Enable = true res.Enable = true
if website.HttpConfig != "" { if website.HttpConfig != "" {
res.HttpConfig = website.HttpConfig res.HttpConfig = website.HttpConfig
@ -648,7 +648,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
} }
var ( var (
res response.WebsiteHTTPS res response.WebsiteHTTPS
websiteSSL model.WebsiteSSL websiteSSL *model.WebsiteSSL
) )
res.Enable = req.Enable res.Enable = req.Enable
res.SSLProtocol = req.SSLProtocol res.SSLProtocol = req.SSLProtocol
@ -698,7 +698,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
return nil, err return nil, err
} }
website.WebsiteSSLID = websiteSSL.ID website.WebsiteSSLID = websiteSSL.ID
res.SSL = websiteSSL res.SSL = *websiteSSL
} }
if req.Type == constant.SSLManual { if req.Type == constant.SSLManual {
var ( var (
@ -758,16 +758,16 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
websiteSSL.PrivateKey = privateKey websiteSSL.PrivateKey = privateKey
websiteSSL.Pem = certificate websiteSSL.Pem = certificate
res.SSL = websiteSSL res.SSL = *websiteSSL
} }
website.Protocol = constant.ProtocolHTTPS website.Protocol = constant.ProtocolHTTPS
if err := applySSL(website, websiteSSL, req); err != nil { if err := applySSL(website, *websiteSSL, req); err != nil {
return nil, err return nil, err
} }
website.HttpConfig = req.HttpConfig website.HttpConfig = req.HttpConfig
if websiteSSL.ID == 0 { if websiteSSL.ID == 0 {
if err := websiteSSLRepo.Create(ctx, &websiteSSL); err != nil { if err := websiteSSLRepo.Create(ctx, websiteSSL); err != nil {
return nil, err return nil, err
} }
website.WebsiteSSLID = websiteSSL.ID website.WebsiteSSLID = websiteSSL.ID

View File

@ -149,48 +149,78 @@ func (w WebsiteCAService) GetCA(id uint) (response.WebsiteCADTO, error) {
} }
func (w WebsiteCAService) Delete(id uint) error { func (w WebsiteCAService) Delete(id uint) error {
ssls, _ := websiteSSLRepo.List(websiteSSLRepo.WithByCAID(id))
if len(ssls) > 0 {
return buserr.New("ErrDeleteCAWithSSL")
}
return websiteCARepo.DeleteBy(commonRepo.WithByID(id)) return websiteCARepo.DeleteBy(commonRepo.WithByID(id))
} }
func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error { func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error {
ca, err := websiteCARepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
newSSL := &model.WebsiteSSL{
Provider: constant.SelfSigned,
KeyType: req.KeyType,
PushDir: req.PushDir,
}
if req.PushDir {
if !files.NewFileOp().Stat(req.Dir) {
return buserr.New(constant.ErrLinkPathNotFound)
}
newSSL.Dir = req.Dir
}
var ( var (
domains []string domains []string
ips []net.IP ips []net.IP
websiteSSL = &model.WebsiteSSL{}
err error
ca model.WebsiteCA
) )
if req.Domains != "" { if req.Renew {
domainArray := strings.Split(req.Domains, "\n") websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
for _, domain := range domainArray { if err != nil {
if !common.IsValidDomain(domain) { return err
err = buserr.WithName("ErrDomainFormat", domain) }
return err ca, err = websiteCARepo.GetFirst(commonRepo.WithByID(websiteSSL.CaID))
if err != nil {
return err
}
existDomains := []string{websiteSSL.PrimaryDomain}
if websiteSSL.Domains != "" {
existDomains = append(existDomains, strings.Split(websiteSSL.Domains, ",")...)
}
for _, domain := range existDomains {
if ipAddress := net.ParseIP(domain); ipAddress == nil {
domains = append(domains, domain)
} else { } else {
if ipAddress := net.ParseIP(domain); ipAddress == nil { ips = append(ips, ipAddress)
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
}
} }
} }
if len(domains) > 0 { } else {
newSSL.PrimaryDomain = domains[0] ca, err = websiteCARepo.GetFirst(commonRepo.WithByID(req.ID))
newSSL.Domains = strings.Join(domains[1:], ",") if err != nil {
return err
}
websiteSSL = &model.WebsiteSSL{
Provider: constant.SelfSigned,
KeyType: req.KeyType,
PushDir: req.PushDir,
CaID: ca.ID,
AutoRenew: req.AutoRenew,
}
if req.PushDir {
if !files.NewFileOp().Stat(req.Dir) {
return buserr.New(constant.ErrLinkPathNotFound)
}
websiteSSL.Dir = req.Dir
}
if req.Domains != "" {
domainArray := strings.Split(req.Domains, "\n")
for _, domain := range domainArray {
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return err
} else {
if ipAddress := net.ParseIP(domain); ipAddress == nil {
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
}
}
}
if len(domains) > 0 {
websiteSSL.PrimaryDomain = domains[0]
websiteSSL.Domains = strings.Join(domains[1:], ",")
}
} }
} }
@ -208,7 +238,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error {
} }
var rootPrivateKey any var rootPrivateKey any
if ssl.KeyType(ca.KeyType) == certcrypto.EC256 || ssl.KeyType(ca.KeyType) == certcrypto.EC384 { if ssl.KeyType(websiteSSL.KeyType) == certcrypto.EC256 || ssl.KeyType(websiteSSL.KeyType) == certcrypto.EC384 {
rootPrivateKey, err = x509.ParseECPrivateKey(rootPrivateKeyBlock.Bytes) rootPrivateKey, err = x509.ParseECPrivateKey(rootPrivateKeyBlock.Bytes)
if err != nil { if err != nil {
return err return err
@ -219,7 +249,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error {
return err return err
} }
} }
interPrivateKey, interPublicKey, _, err := createPrivateKey(req.KeyType) interPrivateKey, interPublicKey, _, err := createPrivateKey(websiteSSL.KeyType)
if err != nil { if err != nil {
return err return err
} }
@ -249,7 +279,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error {
return err return err
} }
_, publicKey, privateKeyBytes, err := createPrivateKey(req.KeyType) _, publicKey, privateKeyBytes, err := createPrivateKey(websiteSSL.KeyType)
if err != nil { if err != nil {
return err return err
} }
@ -281,21 +311,28 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) error {
Bytes: cert.Raw, Bytes: cert.Raw,
} }
pemData := pem.EncodeToMemory(certBlock) pemData := pem.EncodeToMemory(certBlock)
newSSL.Pem = string(pemData) websiteSSL.Pem = string(pemData)
newSSL.PrivateKey = string(privateKeyBytes) websiteSSL.PrivateKey = string(privateKeyBytes)
newSSL.ExpireDate = cert.NotAfter websiteSSL.ExpireDate = cert.NotAfter
newSSL.StartDate = cert.NotBefore websiteSSL.StartDate = cert.NotBefore
newSSL.Type = cert.Issuer.CommonName websiteSSL.Type = cert.Issuer.CommonName
newSSL.Organization = rootCsr.Subject.Organization[0] websiteSSL.Organization = rootCsr.Subject.Organization[0]
if err := websiteSSLRepo.Create(context.Background(), newSSL); err != nil { if req.Renew {
return err if err := websiteSSLRepo.Save(websiteSSL); err != nil {
return err
}
} else {
if err := websiteSSLRepo.Create(context.Background(), websiteSSL); err != nil {
return err
}
} }
logFile, _ := os.OpenFile(path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", newSSL.PrimaryDomain, newSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
logFile, _ := os.OpenFile(path.Join(constant.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
defer logFile.Close() defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags) logger := log.New(logFile, "", log.LstdFlags)
logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")})) logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
saveCertificateFile(*newSSL, logger) saveCertificateFile(websiteSSL, logger)
return nil return nil
} }

View File

@ -11,7 +11,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n" "github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/1Panel-dev/1Panel/backend/utils/files"
@ -35,7 +34,6 @@ type IWebsiteSSLService interface {
GetSSL(id uint) (*response.WebsiteSSLDTO, error) GetSSL(id uint) (*response.WebsiteSSLDTO, error)
Search(req request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error) Search(req request.WebsiteSSLSearch) ([]response.WebsiteSSLDTO, error)
Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error)
Renew(sslId uint) error
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)
GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error)
Delete(ids []uint) error Delete(ids []uint) error
@ -72,7 +70,7 @@ func (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
res.WebsiteSSL = websiteSSL res.WebsiteSSL = *websiteSSL
return &res, nil return &res, nil
} }
@ -157,7 +155,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
var ( var (
err error err error
websiteSSL model.WebsiteSSL websiteSSL *model.WebsiteSSL
acmeAccount *model.WebsiteAcmeAccount acmeAccount *model.WebsiteAcmeAccount
dnsAccount *model.WebsiteDnsAccount dnsAccount *model.WebsiteDnsAccount
) )
@ -257,91 +255,17 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
return nil return nil
} }
func handleError(websiteSSL model.WebsiteSSL, err error) { func handleError(websiteSSL *model.WebsiteSSL, err error) {
if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError { if websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {
websiteSSL.Status = constant.Error websiteSSL.Status = constant.Error
} else { } else {
websiteSSL.Status = constant.SSLApplyError websiteSSL.Status = constant.SSLApplyError
} }
websiteSSL.Message = err.Error() websiteSSL.Message = err.Error()
legoLogger.Logger.Println(i18n.GetErrMsg("ApplySSLFailed", map[string]interface{}{"domain": websiteSSL.PrimaryDomain, "err": err.Error()})) legoLogger.Logger.Println(i18n.GetErrMsg("ApplySSLFailed", map[string]interface{}{"domain": websiteSSL.PrimaryDomain, "detail": err.Error()}))
_ = websiteSSLRepo.Save(websiteSSL) _ = websiteSSLRepo.Save(websiteSSL)
} }
func (w WebsiteSSLService) Renew(sslId uint) error {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(sslId))
if err != nil {
return err
}
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
return err
}
client, err := ssl.NewAcmeClient(acmeAccount)
if err != nil {
return err
}
switch websiteSSL.Provider {
case constant.DNSAccount:
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(websiteSSL.DnsAccountID))
if err != nil {
return err
}
if err := client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
return err
}
case constant.Http:
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
return err
}
case constant.SelfSigned:
}
resource, err := client.RenewSSL(websiteSSL.CertURL)
if err != nil {
return err
}
websiteSSL.PrivateKey = string(resource.PrivateKey)
websiteSSL.Pem = string(resource.Certificate)
websiteSSL.CertURL = resource.CertURL
certBlock, _ := pem.Decode(resource.Certificate)
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return err
}
websiteSSL.ExpireDate = cert.NotAfter
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
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.AppOpenresty)
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) { func (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID)) acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID))
if err != nil { if err != nil {
@ -378,7 +302,7 @@ func (w WebsiteSSLService) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO
if err != nil { if err != nil {
return res, err return res, err
} }
res.WebsiteSSL = websiteSSL res.WebsiteSSL = *websiteSSL
return res, nil return res, nil
} }
@ -391,9 +315,19 @@ func (w WebsiteSSLService) Delete(ids []uint) error {
names = append(names, oldSSL.PrimaryDomain) names = append(names, oldSSL.PrimaryDomain)
} }
continue continue
} else {
_ = websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
} }
sslSetting, _ := settingRepo.Get(settingRepo.WithByKey("SSL"))
if sslSetting.Value == "enable" {
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
idValue, _ := strconv.Atoi(sslID.Value)
if idValue > 0 {
oldSSL, _ := websiteSSLRepo.GetFirst(commonRepo.WithByID(uint(idValue)))
if oldSSL.ID > 0 {
return buserr.New("ErrDeleteWithPanelSSL")
}
}
}
_ = websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
} }
if len(names) > 0 { if len(names) > 0 {
return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ",")) return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ","))
@ -491,7 +425,7 @@ func (w WebsiteSSLService) SyncForRestart() error {
if ssl.Status == constant.SSLApply { if ssl.Status == constant.SSLApply {
ssl.Status = constant.SystemRestart ssl.Status = constant.SystemRestart
ssl.Message = "System restart causing interrupt" ssl.Message = "System restart causing interrupt"
_ = websiteSSLRepo.Save(ssl) _ = websiteSSLRepo.Save(&ssl)
} }
} }
return nil return nil

View File

@ -750,7 +750,7 @@ func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainM
return return
} }
func saveCertificateFile(websiteSSL model.WebsiteSSL, logger *log.Logger) { func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if websiteSSL.PushDir { if websiteSSL.PushDir {
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
var ( var (

View File

@ -1,8 +1,10 @@
package job package job
import ( import (
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/app/service" "github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
"time" "time"
@ -30,9 +32,25 @@ func (ssl *ssl) Run() {
sub := expireDate.Sub(now) sub := expireDate.Sub(now)
if sub.Hours() < 720 { if sub.Hours() < 720 {
global.LOG.Errorf("Update the SSL certificate for the [%s] domain", s.PrimaryDomain) global.LOG.Errorf("Update the SSL certificate for the [%s] domain", s.PrimaryDomain)
if err := sslService.Renew(s.ID); err != nil { if s.Provider == constant.SelfSigned {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error()) caService := service.NewIWebsiteCAService()
continue if err := caService.ObtainSSL(request.WebsiteCAObtain{
ID: s.CaID,
SSLID: s.ID,
Renew: true,
Unit: "year",
Time: 1,
}); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
} else {
if err := sslService.ObtainSSL(request.WebsiteSSLApply{
ID: s.ID,
}); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
} }
global.LOG.Errorf("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain) global.LOG.Errorf("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
} }

View File

@ -103,6 +103,8 @@ ApplySSLFailed: 'Application for [{{ .domain }}] certificate failed, {{.detail}}
ApplySSLSuccess: 'Application for [{{ .domain }}] certificate successful! ! ' ApplySSLSuccess: 'Application for [{{ .domain }}] certificate successful! ! '
DNSAccountName: 'DNS account [{{ .name }}] manufacturer [{{.type}}]' DNSAccountName: 'DNS account [{{ .name }}] manufacturer [{{.type}}]'
PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}' PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "There is an issued certificate under the current organization and cannot be deleted"
ErrDeleteWithPanelSSL: "Panel SSL configuration uses this certificate and cannot be deleted"
#mysql #mysql
ErrUserIsExist: "The current user already exists. Please enter a new user" ErrUserIsExist: "The current user already exists. Please enter a new user"

View File

@ -103,6 +103,8 @@ ApplySSLFailed: '申請 [{{ .domain }}] 憑證失敗, {{.detail}} '
ApplySSLSuccess: '申請 [{{ .domain }}] 憑證成功! ' ApplySSLSuccess: '申請 [{{ .domain }}] 憑證成功! '
DNSAccountName: 'DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]' DNSAccountName: 'DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]'
PushDirLog: '憑證推送到目錄 [{{ .path }}] {{ .status }}' PushDirLog: '憑證推送到目錄 [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "目前機構下存在已簽發證書,無法刪除"
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此證書,無法刪除"
#mysql #mysql

View File

@ -103,6 +103,8 @@ ApplySSLFailed: '申请 [{{ .domain }}] 证书失败, {{.detail}} '
ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功!!' ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功!!'
DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{.type}}]' DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{.type}}]'
PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}' PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: "当前机构下存在已签发证书,无法删除"
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此证书,无法删除"
#mysql #mysql
ErrUserIsExist: "当前用户已存在,请重新输入" ErrUserIsExist: "当前用户已存在,请重新输入"

View File

@ -27,7 +27,7 @@ var AddWebsiteCA = &gormigrate.Migration{
} }
var UpdateWebsiteSSL = &gormigrate.Migration{ var UpdateWebsiteSSL = &gormigrate.Migration{
ID: "20231126-update-website-ssl", ID: "20231127-update-website-ssl",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil { if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err return err

View File

@ -16,5 +16,6 @@ func (a *WebsiteDnsAccountRouter) InitWebsiteCARouter(Router *gin.RouterGroup) {
groupRouter.POST("", baseApi.CreateWebsiteCA) groupRouter.POST("", baseApi.CreateWebsiteCA)
groupRouter.POST("/del", baseApi.DeleteWebsiteCA) groupRouter.POST("/del", baseApi.DeleteWebsiteCA)
groupRouter.POST("/obtain", baseApi.ObtainWebsiteCA) groupRouter.POST("/obtain", baseApi.ObtainWebsiteCA)
groupRouter.POST("/renew", baseApi.RenewWebsiteCA)
} }
} }

View File

@ -16,7 +16,6 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi baseApi := v1.ApiGroupApp.BaseApi
{ {
groupRouter.POST("/search", baseApi.PageWebsiteSSL) groupRouter.POST("/search", baseApi.PageWebsiteSSL)
groupRouter.POST("/renew", baseApi.RenewWebsiteSSL)
groupRouter.POST("", baseApi.CreateWebsiteSSL) groupRouter.POST("", baseApi.CreateWebsiteSSL)
groupRouter.POST("/resolve", baseApi.GetDNSResolve) groupRouter.POST("/resolve", baseApi.GetDNSResolve)
groupRouter.POST("/del", baseApi.DeleteWebsiteSSL) groupRouter.POST("/del", baseApi.DeleteWebsiteSSL)

View File

@ -11119,7 +11119,7 @@ const docTemplate = `{
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "签 SSL 证书", "description": "签 SSL 证书",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -12930,57 +12930,6 @@ const docTemplate = `{
} }
} }
}, },
"/websites/ssl/renew": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "重置网站 ssl",
"consumes": [
"application/json"
],
"tags": [
"Website SSL"
],
"summary": "Reset website ssl",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.WebsiteSSLRenew"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "website_ssls",
"input_column": "id",
"input_value": "SSLId",
"isList": false,
"output_column": "primary_domain",
"output_value": "domain"
}
],
"bodyKeys": [
"SSLId"
],
"formatEN": "Renew ssl [domain]",
"formatZH": "重置 ssl [domain]",
"paramKeys": []
}
}
},
"/websites/ssl/resolve": { "/websites/ssl/resolve": {
"post": { "post": {
"security": [ "security": [
@ -17517,6 +17466,9 @@ const docTemplate = `{
"autoRenew": { "autoRenew": {
"type": "boolean" "type": "boolean"
}, },
"caId": {
"type": "integer"
},
"certURL": { "certURL": {
"type": "string" "type": "string"
}, },
@ -18979,6 +18931,9 @@ const docTemplate = `{
"unit" "unit"
], ],
"properties": { "properties": {
"autoRenew": {
"type": "boolean"
},
"dir": { "dir": {
"type": "string" "type": "string"
}, },
@ -19002,6 +18957,12 @@ const docTemplate = `{
"pushDir": { "pushDir": {
"type": "boolean" "type": "boolean"
}, },
"renew": {
"type": "boolean"
},
"sslID": {
"type": "integer"
},
"time": { "time": {
"type": "integer" "type": "integer"
}, },
@ -19537,17 +19498,6 @@ const docTemplate = `{
} }
} }
}, },
"request.WebsiteSSLRenew": {
"type": "object",
"required": [
"SSLId"
],
"properties": {
"SSLId": {
"type": "integer"
}
}
},
"request.WebsiteSSLSearch": { "request.WebsiteSSLSearch": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -11112,7 +11112,7 @@
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "签 SSL 证书", "description": "签 SSL 证书",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -12923,57 +12923,6 @@
} }
} }
}, },
"/websites/ssl/renew": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "重置网站 ssl",
"consumes": [
"application/json"
],
"tags": [
"Website SSL"
],
"summary": "Reset website ssl",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.WebsiteSSLRenew"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "website_ssls",
"input_column": "id",
"input_value": "SSLId",
"isList": false,
"output_column": "primary_domain",
"output_value": "domain"
}
],
"bodyKeys": [
"SSLId"
],
"formatEN": "Renew ssl [domain]",
"formatZH": "重置 ssl [domain]",
"paramKeys": []
}
}
},
"/websites/ssl/resolve": { "/websites/ssl/resolve": {
"post": { "post": {
"security": [ "security": [
@ -17510,6 +17459,9 @@
"autoRenew": { "autoRenew": {
"type": "boolean" "type": "boolean"
}, },
"caId": {
"type": "integer"
},
"certURL": { "certURL": {
"type": "string" "type": "string"
}, },
@ -18972,6 +18924,9 @@
"unit" "unit"
], ],
"properties": { "properties": {
"autoRenew": {
"type": "boolean"
},
"dir": { "dir": {
"type": "string" "type": "string"
}, },
@ -18995,6 +18950,12 @@
"pushDir": { "pushDir": {
"type": "boolean" "type": "boolean"
}, },
"renew": {
"type": "boolean"
},
"sslID": {
"type": "integer"
},
"time": { "time": {
"type": "integer" "type": "integer"
}, },
@ -19530,17 +19491,6 @@
} }
} }
}, },
"request.WebsiteSSLRenew": {
"type": "object",
"required": [
"SSLId"
],
"properties": {
"SSLId": {
"type": "integer"
}
}
},
"request.WebsiteSSLSearch": { "request.WebsiteSSLSearch": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -2792,6 +2792,8 @@ definitions:
type: integer type: integer
autoRenew: autoRenew:
type: boolean type: boolean
caId:
type: integer
certURL: certURL:
type: string type: string
createdAt: createdAt:
@ -3770,6 +3772,8 @@ definitions:
type: object type: object
request.WebsiteCAObtain: request.WebsiteCAObtain:
properties: properties:
autoRenew:
type: boolean
dir: dir:
type: string type: string
domains: domains:
@ -3787,6 +3791,10 @@ definitions:
type: string type: string
pushDir: pushDir:
type: boolean type: boolean
renew:
type: boolean
sslID:
type: integer
time: time:
type: integer type: integer
unit: unit:
@ -4152,13 +4160,6 @@ definitions:
- primaryDomain - primaryDomain
- provider - provider
type: object type: object
request.WebsiteSSLRenew:
properties:
SSLId:
type: integer
required:
- SSLId
type: object
request.WebsiteSSLSearch: request.WebsiteSSLSearch:
properties: properties:
acmeAccountID: acmeAccountID:
@ -11773,7 +11774,7 @@ paths:
post: post:
consumes: consumes:
- application/json - application/json
description: 签 SSL 证书 description: 签 SSL 证书
parameters: parameters:
- description: request - description: request
in: body in: body
@ -12904,39 +12905,6 @@ paths:
formatEN: apply ssl [domain] formatEN: apply ssl [domain]
formatZH: 申请证书 [domain] formatZH: 申请证书 [domain]
paramKeys: [] paramKeys: []
/websites/ssl/renew:
post:
consumes:
- application/json
description: 重置网站 ssl
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.WebsiteSSLRenew'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Reset website ssl
tags:
- Website SSL
x-panel-log:
BeforeFunctions:
- db: website_ssls
input_column: id
input_value: SSLId
isList: false
output_column: primary_domain
output_value: domain
bodyKeys:
- SSLId
formatEN: Renew ssl [domain]
formatZH: 重置 ssl [domain]
paramKeys: []
/websites/ssl/resolve: /websites/ssl/resolve:
post: post:
consumes: consumes:

View File

@ -165,10 +165,10 @@ export namespace Website {
autoRenew: boolean; autoRenew: boolean;
acmeAccountId?: number; acmeAccountId?: number;
status: string; status: string;
domains: string;
} }
export interface SSLDTO extends SSL { export interface SSLDTO extends SSL {
domains: string;
logPath: string; logPath: string;
} }
@ -485,4 +485,8 @@ export namespace Website {
pushDir: boolean; pushDir: boolean;
dir: string; dir: string;
} }
export interface RenewSSLByCA {
SSLID: number;
}
} }

View File

@ -124,10 +124,6 @@ export const ObtainSSL = (req: Website.SSLObtain) => {
return http.post<any>(`/websites/ssl/obtain`, req); return http.post<any>(`/websites/ssl/obtain`, req);
}; };
export const RenewSSL = (req: Website.SSLRenew) => {
return http.post<any>(`/websites/ssl/renew`, req, TimeoutEnum.T_10M);
};
export const UpdateSSL = (req: Website.SSLUpdate) => { export const UpdateSSL = (req: Website.SSLUpdate) => {
return http.post<any>(`/websites/ssl/update`, req); return http.post<any>(`/websites/ssl/update`, req);
}; };
@ -263,3 +259,7 @@ export const ObtainSSLByCA = (req: Website.SSLObtainByCA) => {
export const DeleteCA = (req: Website.DelReq) => { export const DeleteCA = (req: Website.DelReq) => {
return http.post<any>(`/websites/ca/del`, req); return http.post<any>(`/websites/ca/del`, req);
}; };
export const RenewSSLByCA = (req: Website.RenewSSLByCA) => {
return http.post<any>(`/websites/ca/renew`, req);
};

View File

@ -37,6 +37,7 @@ interface LogProps {
type: string; type: string;
style: string; style: string;
name: string; name: string;
tail: boolean;
} }
const open = ref(false); const open = ref(false);

View File

@ -46,6 +46,7 @@ interface LogProps {
id?: number; id?: number;
type: string; type: string;
name?: string; name?: string;
tail?: boolean;
} }
const props = defineProps({ const props = defineProps({
@ -55,6 +56,7 @@ const props = defineProps({
id: 0, id: 0,
type: '', type: '',
name: '', name: '',
tail: false,
}), }),
}, },
style: { style: {
@ -224,9 +226,16 @@ function isScrolledToBottom(element: HTMLElement): boolean {
} }
const init = () => { const init = () => {
tailLog.value = false; if (props.config.tail) {
tailLog.value = props.config.tail;
} else {
tailLog.value = false;
}
if (tailLog.value) {
changeTail(false);
}
getContent(); getContent();
nextTick(() => { nextTick(() => {
if (scrollerElement.value) { if (scrollerElement.value) {
scrollerElement.value.addEventListener('scroll', function () { scrollerElement.value.addEventListener('scroll', function () {

View File

@ -1809,12 +1809,12 @@ const message = {
key: 'Private Key', key: 'Private Key',
startDate: 'Effective Time', startDate: 'Effective Time',
organization: 'issuing organization', organization: 'issuing organization',
renewConfirm: 'Are you sure to renew? ', renewConfirm: 'Are you sure you want to apply for a certificate for domain name {0}? ',
autoRenew: 'Automatic renewal', autoRenew: 'Automatic renewal',
autoRenewHelper: 'Automatic renewal 7 days from the expiration time', autoRenewHelper: 'Automatically renew 30 days before expiration',
renewSuccess: 'Renewal succeeded', renewSuccess: 'Renewal successful',
renewWebsite: renewWebsite:
'This certificate has been associated with the following websites, and the renewal will be applied to these websites simultaneously', 'This certificate has been associated with the following websites, and the application will be applied to these websites simultaneously',
createAcme: 'Create Account', createAcme: 'Create Account',
acmeHelper: 'Acme account is used to apply for free certificates', acmeHelper: 'Acme account is used to apply for free certificates',
upload: 'Upload Certificate', upload: 'Upload Certificate',

View File

@ -1698,11 +1698,11 @@ const message = {
key: '私鑰', key: '私鑰',
startDate: '生效時間', startDate: '生效時間',
organization: '簽發機構', organization: '簽發機構',
renewConfirm: '是否確定續簽', renewConfirm: '是否確定給網域名稱 {0} 申請證書 ',
autoRenew: '自動續簽', autoRenew: '自動續簽',
autoRenewHelper: '距離到期時間7天自動續簽', autoRenewHelper: '距離到期時間30天自動續約',
renewSuccess: '續簽成功', renewSuccess: '續簽成功',
renewWebsite: '該證書已經和以下網站關聯續簽會同步應用到這些網站', renewWebsite: '該證書已經和以下網站關聯申請會同步應用到這些網站',
createAcme: '創建賬戶', createAcme: '創建賬戶',
acmeHelper: 'Acme 賬戶用於申請免費證書', acmeHelper: 'Acme 賬戶用於申請免費證書',
upload: '上傳證書', upload: '上傳證書',

View File

@ -1698,11 +1698,11 @@ const message = {
key: '私钥', key: '私钥',
startDate: '生效时间', startDate: '生效时间',
organization: '签发机构', organization: '签发机构',
renewConfirm: '是否确定续签', renewConfirm: '是否确定给域名 {0} 申请证书',
autoRenew: '自动续签', autoRenew: '自动续签',
autoRenewHelper: '距离到期时间7天自动续签', autoRenewHelper: '距离到期时间30天自动续签',
renewSuccess: '续签成功', renewSuccess: '续签成功',
renewWebsite: '该证书已经和以下网站关联续签会同步应用到这些网站', renewWebsite: '该证书已经和以下网站关联申请会同步应用到这些网站',
createAcme: '创建账户', createAcme: '创建账户',
acmeHelper: 'Acme 账户用于申请免费证书', acmeHelper: 'Acme 账户用于申请免费证书',
upload: '上传证书', upload: '上传证书',

View File

@ -47,7 +47,7 @@ const open = ref(false);
const loading = ref(false); const loading = ref(false);
const dnsResolve = ref<Website.DNSResolve[]>([]); const dnsResolve = ref<Website.DNSResolve[]>([]);
const sslID = ref(0); const sslID = ref(0);
const em = defineEmits(['close']); const em = defineEmits(['close', 'submit']);
const handleClose = () => { const handleClose = () => {
open.value = false; open.value = false;
em('close', false); em('close', false);
@ -83,6 +83,7 @@ const submit = () => {
.then(() => { .then(() => {
MsgSuccess(i18n.global.t('ssl.applyStart')); MsgSuccess(i18n.global.t('ssl.applyStart'));
handleClose(); handleClose();
em('submit', sslID.value);
}) })
.finally(() => {}); .finally(() => {});
}; };

View File

@ -37,6 +37,9 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="''" prop="autoRenew">
<el-checkbox v-model="obtain.autoRenew" :label="$t('ssl.autoRenew')" />
</el-form-item>
<el-form-item :label="''" prop="pushDir"> <el-form-item :label="''" prop="pushDir">
<el-checkbox v-model="obtain.pushDir" :label="$t('ssl.pushDir')" /> <el-checkbox v-model="obtain.pushDir" :label="$t('ssl.pushDir')" />
</el-form-item> </el-form-item>
@ -93,6 +96,7 @@ const initData = () => ({
unit: 'day', unit: 'day',
pushDir: false, pushDir: false,
dir: '', dir: '',
autoRenew: true,
}); });
const obtain = ref(initData()); const obtain = ref(initData());

View File

@ -96,7 +96,9 @@
</el-table-column> </el-table-column>
<el-table-column :label="$t('website.log')" width="100px"> <el-table-column :label="$t('website.log')" width="100px">
<template #default="{ row }"> <template #default="{ row }">
<el-button @click="openLog(row)" link type="primary">{{ $t('website.check') }}</el-button> <el-button @click="openLog(row.id)" link type="primary">
{{ $t('website.check') }}
</el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -108,11 +110,7 @@
<el-table-column :label="$t('ssl.autoRenew')" fix width="100px"> <el-table-column :label="$t('ssl.autoRenew')" fix width="100px">
<template #default="{ row }"> <template #default="{ row }">
<el-switch <el-switch
:disabled=" :disabled="row.provider === 'dnsManual' || row.provider === 'manual'"
row.provider === 'dnsManual' ||
row.provider === 'manual' ||
row.provider === 'selfSigned'
"
v-model="row.autoRenew" v-model="row.autoRenew"
@change="updateConfig(row)" @change="updateConfig(row)"
/> />
@ -138,10 +136,11 @@
<Create ref="sslCreateRef" @close="search()"></Create> <Create ref="sslCreateRef" @close="search()"></Create>
<Detail ref="detailRef"></Detail> <Detail ref="detailRef"></Detail>
<SSLUpload ref="sslUploadRef" @close="search()"></SSLUpload> <SSLUpload ref="sslUploadRef" @close="search()"></SSLUpload>
<Apply ref="applyRef" @search="search" /> <Apply ref="applyRef" @search="search" @submit="openLog" />
<OpDialog ref="opRef" @search="search" @cancel="search" /> <OpDialog ref="opRef" @search="search" @cancel="search" />
<Log ref="logRef" @close="search()" /> <Log ref="logRef" @close="search()" />
<CA ref="caRef" @close="search()" /> <CA ref="caRef" @close="search()" />
<Obtain ref="obtainRef" @close="search()" @submit="openLog" />
</LayoutContent> </LayoutContent>
</div> </div>
</template> </template>
@ -149,7 +148,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, reactive, ref, computed } from 'vue'; import { onMounted, reactive, ref, computed } from 'vue';
import OpDialog from '@/components/del-dialog/index.vue'; import OpDialog from '@/components/del-dialog/index.vue';
import { DeleteSSL, ObtainSSL, SearchSSL, UpdateSSL } from '@/api/modules/website'; import { DeleteSSL, SearchSSL, UpdateSSL } from '@/api/modules/website';
import DnsAccount from './dns-account/index.vue'; import DnsAccount from './dns-account/index.vue';
import AcmeAccount from './acme-account/index.vue'; import AcmeAccount from './acme-account/index.vue';
import CA from './ca/index.vue'; import CA from './ca/index.vue';
@ -163,6 +162,7 @@ import { GlobalStore } from '@/store';
import SSLUpload from './upload/index.vue'; import SSLUpload from './upload/index.vue';
import Apply from './apply/index.vue'; import Apply from './apply/index.vue';
import Log from '@/components/log-dialog/index.vue'; import Log from '@/components/log-dialog/index.vue';
import Obtain from './obtain/index.vue';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const paginationConfig = reactive({ const paginationConfig = reactive({
@ -183,6 +183,7 @@ const applyRef = ref();
const logRef = ref(); const logRef = ref();
const caRef = ref(); const caRef = ref();
let selects = ref<any>([]); let selects = ref<any>([]);
const obtainRef = ref();
const routerButton = [ const routerButton = [
{ {
@ -204,7 +205,7 @@ const buttons = [
{ {
label: i18n.global.t('ssl.apply'), label: i18n.global.t('ssl.apply'),
disabled: function (row: Website.SSLDTO) { disabled: function (row: Website.SSLDTO) {
return row.status === 'applying' || row.provider === 'manual' || row.provider === 'selfSigned'; return row.status === 'applying' || row.provider === 'manual';
}, },
click: function (row: Website.SSLDTO) { click: function (row: Website.SSLDTO) {
if (row.provider === 'dnsManual') { if (row.provider === 'dnsManual') {
@ -268,23 +269,16 @@ const openUpload = () => {
const openDetail = (id: number) => { const openDetail = (id: number) => {
detailRef.value.acceptParams(id); detailRef.value.acceptParams(id);
}; };
const openLog = (row: Website.SSLDTO) => { const openLog = (id: number) => {
logRef.value.acceptParams({ id: row.id, type: 'ssl' }); logRef.value.acceptParams({ id: id, type: 'ssl', tail: true });
}; };
const openCA = () => { const openCA = () => {
caRef.value.acceptParams(); caRef.value.acceptParams();
}; };
const applySSL = (row: Website.SSLDTO) => { const applySSL = (row: Website.SSLDTO) => {
loading.value = true; obtainRef.value.acceptParams({ ssl: row });
ObtainSSL({ ID: row.id })
.then(() => {
MsgSuccess(i18n.global.t('ssl.applyStart'));
search();
})
.finally(() => {
loading.value = false;
});
}; };
const deleteSSL = async (row: any) => { const deleteSSL = async (row: any) => {

View File

@ -1,26 +1,26 @@
<template> <template>
<el-dialog <el-dialog
v-model="open" v-model="open"
:title="$t('website.renewSSL')" :title="$t('ssl.apply')"
:destroy-on-close="true" :destroy-on-close="true"
:close-on-click-modal="false" :close-on-click-modal="false"
width="30%" width="30%"
:before-close="handleClose" :before-close="handleClose"
> >
<div style="text-align: center" v-loading="loading"> <div class="text-center" v-loading="loading">
<div v-if="websites.length > 0"> <div v-if="ssl.websites && ssl.websites.length > 0">
<span>{{ $t('ssl.renewWebsite') }}</span> <span>{{ $t('ssl.renewWebsite') }}</span>
<div> <div>
<br /> <br />
<span> <span>
<span v-for="(website, index) in websites" :key="index"> <span v-for="(website, index) in ssl.websites" :key="index">
<el-tag>{{ website.primaryDomain }}</el-tag> <el-tag>{{ website.primaryDomain }}</el-tag>
</span> </span>
</span> </span>
</div> </div>
<br /> <br />
</div> </div>
<span>{{ $t('ssl.renewConfirm') }}</span> <span>{{ $t('ssl.renewConfirm', [ssl.primaryDomain]) }}</span>
</div> </div>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
@ -35,44 +35,42 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Website } from '@/api/interface/website'; import { Website } from '@/api/interface/website';
import { RenewSSL } from '@/api/modules/website'; import { ObtainSSL, RenewSSLByCA } from '@/api/modules/website';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { reactive, ref } from 'vue'; import { ref } from 'vue';
interface RenewProps { interface RenewProps {
id: number; ssl: Website.SSL;
websites: Website.Website[];
} }
let open = ref(false); const open = ref(false);
let loading = ref(false); const loading = ref(false);
let renewReq = reactive({ const em = defineEmits(['close', 'submit']);
SSLId: 0,
});
const em = defineEmits(['close']);
const handleClose = () => { const handleClose = () => {
open.value = false; open.value = false;
em('close', false); em('close', false);
}; };
const websites = ref([]); const ssl = ref();
const acceptParams = async (props: RenewProps) => { const acceptParams = async (props: RenewProps) => {
renewReq.SSLId = Number(props.id); ssl.value = props.ssl;
websites.value = props.websites;
open.value = true; open.value = true;
}; };
const submit = () => { const submit = async () => {
loading.value = true; loading.value = true;
RenewSSL(renewReq) try {
.then(() => { if (ssl.value.provider == 'selfSigned') {
handleClose(); await RenewSSLByCA({ SSLID: ssl.value.id });
MsgSuccess(i18n.global.t('ssl.renewSuccess')); } else {
}) await ObtainSSL({ ID: ssl.value.id });
.finally(() => { }
loading.value = false; handleClose();
}); MsgSuccess(i18n.global.t('ssl.applyStart'));
loading.value = false;
em('submit', ssl.value.id);
} catch (error) {}
}; };
defineExpose({ defineExpose({