mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 00:09:16 +08:00
feat: 增加证书申请
This commit is contained in:
parent
ac864ff4e7
commit
681a0c9106
@ -12,6 +12,7 @@ services:
|
||||
- ./conf/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./log:/var/log/nginx
|
||||
- ./conf/conf.d:/etc/nginx/conf.d/
|
||||
- ./ssl:/etc/nginx/ssl
|
||||
|
||||
networks:
|
||||
1panel:
|
||||
|
@ -24,43 +24,59 @@ func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
//func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
|
||||
// var req dto.WebsiteSSLCreate
|
||||
// if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
// return
|
||||
// }
|
||||
// if _, err := WebsiteSSLService.Create(req); err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
// return
|
||||
// }
|
||||
// helper.SuccessWithData(c, nil)
|
||||
//}
|
||||
//
|
||||
//func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
|
||||
// var req dto.WebsiteSSLUpdate
|
||||
// if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
// return
|
||||
// }
|
||||
// if _, err := WebsiteSSLService.Update(req); err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
// return
|
||||
// }
|
||||
// helper.SuccessWithData(c, nil)
|
||||
//}
|
||||
//
|
||||
//func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
//
|
||||
// id, err := helper.GetParamID(c)
|
||||
// if err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if err := WebsiteSSLService.Delete(id); err != nil {
|
||||
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
// return
|
||||
// }
|
||||
// helper.SuccessWithData(c, nil)
|
||||
//}
|
||||
func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
|
||||
var req dto.WebsiteSSLCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteSSLService.Create(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {
|
||||
var req dto.WebsiteSSLApply
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteSSLService.Apply(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetDNSResolve(c *gin.Context) {
|
||||
var req dto.WebsiteDNSReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteSSLService.GetDNSResolve(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
|
||||
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := websiteSSLService.Delete(id); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type NginxScope string
|
||||
const (
|
||||
Index NginxScope = "index"
|
||||
LimitConn NginxScope = "limit-conn"
|
||||
SSL NginxScope = "ssl"
|
||||
)
|
||||
|
||||
type NginxOp string
|
||||
@ -34,6 +35,7 @@ const (
|
||||
var ScopeKeyMap = map[NginxScope][]string{
|
||||
Index: {"index"},
|
||||
LimitConn: {"limit_conn", "limit_rate", "limit_conn_zone"},
|
||||
SSL: {"ssl_certificate", "ssl_certificate_key"},
|
||||
}
|
||||
|
||||
var RepeatKeys = map[string]struct {
|
||||
|
@ -28,6 +28,10 @@ type WebSiteCreate struct {
|
||||
WebSiteGroupID uint `json:"webSiteGroupID" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteDTO struct {
|
||||
model.WebSite
|
||||
}
|
||||
|
||||
type WebSiteUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
PrimaryDomain string `json:"primaryDomain" validate:"required"`
|
||||
|
@ -6,8 +6,34 @@ type WebsiteSSLDTO struct {
|
||||
model.WebSiteSSL
|
||||
}
|
||||
|
||||
type SSLProvider string
|
||||
|
||||
const (
|
||||
DNSAccount = "dnsAccount"
|
||||
DnsCommon = "dnsCommon"
|
||||
Http = "http"
|
||||
)
|
||||
|
||||
type WebsiteSSLCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Authorization map[string]string `json:"authorization" validate:"required"`
|
||||
Domains []string `json:"domains" validate:"required"`
|
||||
Provider SSLProvider `json:"provider" validate:"required"`
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
AcmeAccountID uint `json:"acmeAccountId"`
|
||||
DnsAccountID uint `json:"dnsAccountId"`
|
||||
}
|
||||
|
||||
type WebsiteSSLApply struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
SSLID uint `json:"SSLId" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteDNSReq struct {
|
||||
Domains []string `json:"domains" validate:"required"`
|
||||
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteDNSRes struct {
|
||||
Key string `json:"resolve"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
@ -4,17 +4,18 @@ import "time"
|
||||
|
||||
type WebSite struct {
|
||||
BaseModel
|
||||
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
WebsiteDnsAccountID uint `gorm:"type:integer" json:"websiteDnsAccountId"`
|
||||
WebsiteAcmeAccountID uint `gorm:"type:integer" json:"websiteAcmeAccountId"`
|
||||
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
WebsiteDnsAccountID uint `gorm:"type:integer" json:"websiteDnsAccountId"`
|
||||
WebsiteAcmeAccountID uint `gorm:"type:integer" json:"websiteAcmeAccountId"`
|
||||
Domains []WebSiteDomain `json:"domains"`
|
||||
}
|
||||
|
||||
func (w WebSite) TableName() string {
|
||||
|
@ -4,14 +4,16 @@ import "time"
|
||||
|
||||
type WebSiteSSL struct {
|
||||
BaseModel
|
||||
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
|
||||
Pem string `gorm:"type:longtext;not null" json:"pem"`
|
||||
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
|
||||
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
IssuerName string `gorm:"type:varchar(64);not null" json:"issuerName"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
Alias string `gorm:"type:varchar(64);not null" json:"alias"`
|
||||
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
|
||||
Pem string `gorm:"type:longtext;not null" json:"pem"`
|
||||
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
|
||||
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
IssuerName string `gorm:"type:varchar(64);not null" json:"issuerName"`
|
||||
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
StartDate time.Time `json:"startDate"`
|
||||
}
|
||||
|
||||
func (w WebSiteSSL) TableName() string {
|
||||
|
@ -28,7 +28,7 @@ func (w WebSiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebS
|
||||
func (w WebSiteRepo) GetFirst(opts ...DBOption) (model.WebSite, error) {
|
||||
var website model.WebSite
|
||||
db := getDb(opts...).Model(&model.WebSite{})
|
||||
if err := db.First(&website).Error; err != nil {
|
||||
if err := db.Preload("Domains").First(&website).Error; err != nil {
|
||||
return website, err
|
||||
}
|
||||
return website, nil
|
||||
|
@ -14,6 +14,15 @@ func (w WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, [
|
||||
return count, accounts, err
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccountRepo) GetFirst(opts ...DBOption) (model.WebsiteAcmeAccount, error) {
|
||||
var account model.WebsiteAcmeAccount
|
||||
db := getDb(opts...).Model(&model.WebsiteAcmeAccount{})
|
||||
if err := db.First(&account).Error; err != nil {
|
||||
return account, err
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (w WebsiteAcmeAccountRepo) Create(account model.WebsiteAcmeAccount) error {
|
||||
return getDb().Create(&account).Error
|
||||
}
|
||||
|
@ -16,6 +16,15 @@ func (w WebsiteDnsAccountRepo) Page(page, size int, opts ...DBOption) (int64, []
|
||||
return count, accounts, err
|
||||
}
|
||||
|
||||
func (w WebsiteDnsAccountRepo) GetFirst(opts ...DBOption) (model.WebsiteDnsAccount, error) {
|
||||
var account model.WebsiteDnsAccount
|
||||
db := getDb(opts...).Model(&model.WebsiteDnsAccount{})
|
||||
if err := db.First(&account).Error; err != nil {
|
||||
return account, err
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (w WebsiteDnsAccountRepo) Create(account model.WebsiteDnsAccount) error {
|
||||
return getDb().Create(&account).Error
|
||||
}
|
||||
|
@ -1,10 +1,20 @@
|
||||
package repo
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type WebsiteSSLRepo struct {
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) ByAlias(alias string) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("alias = ?", alias)
|
||||
}
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteSSL, error) {
|
||||
var sslList []model.WebSiteSSL
|
||||
db := getDb(opts...).Model(&model.WebSiteSSL{})
|
||||
@ -14,8 +24,17 @@ func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.W
|
||||
return count, sslList, err
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Create(ssl model.WebSiteSSL) error {
|
||||
return getDb().Create(&ssl).Error
|
||||
func (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (model.WebSiteSSL, error) {
|
||||
var website model.WebSiteSSL
|
||||
db := getDb(opts...).Model(&model.WebSiteSSL{})
|
||||
if err := db.First(&website).Error; err != nil {
|
||||
return website, err
|
||||
}
|
||||
return website, nil
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebSiteSSL) error {
|
||||
return getTx(ctx).Create(ssl).Error
|
||||
}
|
||||
|
||||
func (w WebsiteSSLRepo) Save(ssl model.WebSiteSSL) error {
|
||||
|
@ -99,12 +99,14 @@ func (w WebsiteService) UpdateWebsite(req dto.WebSiteUpdate) error {
|
||||
return websiteRepo.Save(context.TODO(), &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetWebsite(id uint) (model.WebSite, error) {
|
||||
func (w WebsiteService) GetWebsite(id uint) (dto.WebsiteDTO, error) {
|
||||
var res dto.WebsiteDTO
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return website, err
|
||||
return res, err
|
||||
}
|
||||
return website, nil
|
||||
res.WebSite = website
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) DeleteWebSite(req dto.WebSiteDel) error {
|
||||
|
@ -1,7 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WebSiteSSLService struct {
|
||||
@ -18,44 +24,115 @@ func (w WebSiteSSLService) Page(search dto.PageInfo) (int64, []dto.WebsiteSSLDTO
|
||||
return total, sslDTOs, err
|
||||
}
|
||||
|
||||
//func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
|
||||
//
|
||||
// authorization, err := json.Marshal(create.Authorization)
|
||||
// if err != nil {
|
||||
// return dto.WebsiteSSLCreate{}, err
|
||||
// }
|
||||
//
|
||||
// if err := websiteSSLRepo.Create(model.WebsiteDnsAccount{
|
||||
// Name: create.Name,
|
||||
// Type: create.Type,
|
||||
// Authorization: string(authorization),
|
||||
// }); err != nil {
|
||||
// return dto.WebsiteSSLCreate{}, err
|
||||
// }
|
||||
//
|
||||
// return create, nil
|
||||
//}
|
||||
//
|
||||
//func (w WebSiteSSLService) Update(update dto.WebsiteDnsAccountUpdate) (dto.WebsiteDnsAccountUpdate, error) {
|
||||
//
|
||||
// authorization, err := json.Marshal(update.Authorization)
|
||||
// if err != nil {
|
||||
// return dto.WebsiteDnsAccountUpdate{}, err
|
||||
// }
|
||||
//
|
||||
// if err := websiteSSLRepo.Save(model.WebsiteDnsAccount{
|
||||
// BaseModel: model.BaseModel{
|
||||
// ID: update.ID,
|
||||
// },
|
||||
// Name: update.Name,
|
||||
// Type: update.Type,
|
||||
// Authorization: string(authorization),
|
||||
// }); err != nil {
|
||||
// return dto.WebsiteDnsAccountUpdate{}, err
|
||||
// }
|
||||
//
|
||||
// return update, nil
|
||||
//}
|
||||
func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
|
||||
|
||||
var res dto.WebsiteSSLCreate
|
||||
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(create.AcmeAccountID))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(create.DnsAccountID))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if create.Provider == dto.Http {
|
||||
|
||||
} else {
|
||||
if err := client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
resource, err := client.GetSSL(create.Domains)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
var websiteSSL model.WebSiteSSL
|
||||
|
||||
//TODO 判断同一个账号下的证书
|
||||
websiteSSL.Alias = create.Domains[0]
|
||||
websiteSSL.Domain = strings.Join(create.Domains, ",")
|
||||
websiteSSL.PrivateKey = string(resource.PrivateKey)
|
||||
websiteSSL.Pem = string(resource.Certificate)
|
||||
websiteSSL.CertURL = resource.CertURL
|
||||
|
||||
cert, err := x509.ParseCertificate([]byte(websiteSSL.Pem))
|
||||
if err != nil {
|
||||
return dto.WebsiteSSLCreate{}, err
|
||||
}
|
||||
websiteSSL.ExpireDate = cert.NotAfter
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.IssuerName = cert.Issuer.Organization[0]
|
||||
|
||||
if err := createPemFile(websiteSSL); err != nil {
|
||||
return dto.WebsiteSSLCreate{}, err
|
||||
}
|
||||
|
||||
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return create, nil
|
||||
}
|
||||
|
||||
func (w WebSiteSSLService) Apply(apply dto.WebsiteSSLApply) (dto.WebsiteSSLApply, error) {
|
||||
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(apply.SSLID))
|
||||
if err != nil {
|
||||
return dto.WebsiteSSLApply{}, err
|
||||
}
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(apply.WebsiteID))
|
||||
if err != nil {
|
||||
return dto.WebsiteSSLApply{}, err
|
||||
}
|
||||
if err := createPemFile(websiteSSL); err != nil {
|
||||
return dto.WebsiteSSLApply{}, err
|
||||
}
|
||||
nginxParams := getNginxParamsFromStaticFile(dto.SSL)
|
||||
for i, param := range nginxParams {
|
||||
if param.Name == "ssl_certificate" {
|
||||
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.Alias, "fullchain.pem")}
|
||||
}
|
||||
if param.Name == "ssl_certificate_key" {
|
||||
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", websiteSSL.Alias, "privkey.pem")}
|
||||
}
|
||||
}
|
||||
if err := updateNginxConfig(website, nginxParams, dto.SSL); err != nil {
|
||||
return dto.WebsiteSSLApply{}, err
|
||||
}
|
||||
website.WebSiteSSLID = websiteSSL.ID
|
||||
if err := websiteRepo.Save(context.TODO(), &website); err != nil {
|
||||
return dto.WebsiteSSLApply{}, err
|
||||
}
|
||||
|
||||
return apply, nil
|
||||
}
|
||||
|
||||
func (w WebSiteSSLService) GetDNSResolve(req dto.WebsiteDNSReq) (dto.WebsiteDNSRes, error) {
|
||||
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(req.AcmeAccountID))
|
||||
if err != nil {
|
||||
return dto.WebsiteDNSRes{}, err
|
||||
}
|
||||
|
||||
client, err := ssl.NewPrivateKeyClient(acmeAccount.Email, acmeAccount.PrivateKey)
|
||||
if err != nil {
|
||||
return dto.WebsiteDNSRes{}, err
|
||||
}
|
||||
re, err := client.UseManualDns(req.Domains)
|
||||
if err != nil {
|
||||
return dto.WebsiteDNSRes{}, err
|
||||
}
|
||||
var res dto.WebsiteDNSRes
|
||||
res.Key = re.Key
|
||||
res.Value = re.Value
|
||||
res.Type = "TXT"
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebSiteSSLService) Delete(id uint) error {
|
||||
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -258,16 +259,38 @@ func updateNginxConfig(website model.WebSite, params []dto.NginxParam, scope dto
|
||||
}
|
||||
|
||||
func updateConfig(config *components.Config, scope dto.NginxScope) {
|
||||
if scope == dto.LimitConn {
|
||||
limit := parser.NewStringParser(string(nginx_conf.Limit)).Parse()
|
||||
for _, dir := range limit.GetDirectives() {
|
||||
newDir := components.Directive{
|
||||
Name: dir.GetName(),
|
||||
Parameters: dir.GetParameters(),
|
||||
}
|
||||
config.UpdateDirectiveBySecondKey(dir.GetName(), dir.GetParameters()[0], newDir)
|
||||
}
|
||||
newConfig := &components.Config{}
|
||||
switch scope {
|
||||
case dto.LimitConn:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.Limit)).Parse()
|
||||
}
|
||||
if reflect.DeepEqual(newConfig, &components.Config{}) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dir := range newConfig.GetDirectives() {
|
||||
newDir := components.Directive{
|
||||
Name: dir.GetName(),
|
||||
Parameters: dir.GetParameters(),
|
||||
}
|
||||
config.UpdateDirectiveBySecondKey(dir.GetName(), dir.GetParameters()[0], newDir)
|
||||
}
|
||||
}
|
||||
|
||||
func getNginxParamsFromStaticFile(scope dto.NginxScope) []dto.NginxParam {
|
||||
var nginxParams []dto.NginxParam
|
||||
newConfig := &components.Config{}
|
||||
switch scope {
|
||||
case dto.SSL:
|
||||
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
|
||||
}
|
||||
for _, dir := range newConfig.GetDirectives() {
|
||||
nginxParams = append(nginxParams, dto.NginxParam{
|
||||
Name: dir.GetName(),
|
||||
Params: dir.GetParameters(),
|
||||
})
|
||||
}
|
||||
return nginxParams
|
||||
}
|
||||
|
||||
func deleteNginxConfig(website model.WebSite, keys []string) error {
|
||||
@ -285,6 +308,48 @@ func deleteNginxConfig(website model.WebSite, keys []string) error {
|
||||
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
|
||||
}
|
||||
|
||||
func createPemFile(websiteSSL model.WebSiteSSL) error {
|
||||
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "ssl", websiteSSL.Alias)
|
||||
fileOp := files.NewFileOp()
|
||||
|
||||
if !fileOp.Stat(configDir) {
|
||||
if err := fileOp.CreateDir(configDir, 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fullChainFile := path.Join(configDir, "fullchain.pem")
|
||||
privatePemFile := path.Join(configDir, "privkey.pem")
|
||||
|
||||
if !fileOp.Stat(fullChainFile) {
|
||||
if err := fileOp.CreateFile(fullChainFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !fileOp.Stat(privatePemFile) {
|
||||
if err := fileOp.CreateFile(privatePemFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := fileOp.WriteFile(fullChainFile, strings.NewReader(websiteSSL.Pem), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.WriteFile(privatePemFile, strings.NewReader(websiteSSL.PrivateKey), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getParamArray(key string, param interface{}) []string {
|
||||
var res []string
|
||||
switch param.(type) {
|
||||
|
@ -83,6 +83,7 @@ func Routers() *gin.Engine {
|
||||
systemRouter.InitWebsiteGroupRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteDnsAccountRouter(PrivateGroup)
|
||||
systemRouter.InitDatabaseRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteSSLRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteAcmeAccountRouter(PrivateGroup)
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ type RouterGroup struct {
|
||||
WebsiteGroupRouter
|
||||
WebsiteDnsAccountRouter
|
||||
WebsiteAcmeAccountRouter
|
||||
WebsiteSSLRouter
|
||||
DatabaseRouter
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ func (a *WebsiteDnsAccountRouter) InitWebsiteDnsAccountRouter(Router *gin.Router
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
groupRouter.POST("", baseApi.PageWebsiteDnsAccount)
|
||||
groupRouter.POST("/create", baseApi.CreateWebsiteDnsAccount)
|
||||
groupRouter.POST("/search", baseApi.PageWebsiteDnsAccount)
|
||||
groupRouter.POST("", baseApi.CreateWebsiteDnsAccount)
|
||||
groupRouter.POST("/update", baseApi.UpdateWebsiteDnsAccount)
|
||||
groupRouter.DELETE("/:id", baseApi.DeleteWebsiteDnsAccount)
|
||||
}
|
||||
|
@ -15,7 +15,10 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
groupRouter.POST("", baseApi.PageWebsiteSSL)
|
||||
|
||||
groupRouter.POST("/search", baseApi.PageWebsiteSSL)
|
||||
groupRouter.POST("", baseApi.CreateWebsiteSSL)
|
||||
groupRouter.POST("/apply", baseApi.ApplyWebsiteSSL)
|
||||
groupRouter.POST("/resolve", baseApi.GetDNSResolve)
|
||||
groupRouter.POST("/:id", baseApi.DeleteWebsiteSSL)
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
-----BEGIN privateKey-----
|
||||
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
|
||||
-----END privateKey-----
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dnspod"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/pkg/errors"
|
||||
@ -58,7 +59,8 @@ func NewAcmeClient(email, privateKey string) (*AcmeClient, error) {
|
||||
type DnsType string
|
||||
|
||||
const (
|
||||
DnsPod DnsType = "dnsPod"
|
||||
DnsPod DnsType = "DnsPod"
|
||||
AliYun DnsType = "AliYun"
|
||||
)
|
||||
|
||||
type DNSParam struct {
|
||||
@ -87,9 +89,37 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dnsType == AliYun {
|
||||
alidnsConfig := alidns.NewDefaultConfig()
|
||||
alidnsConfig.SecretKey = param.SecretKey
|
||||
alidnsConfig.APIKey = param.AccessKey
|
||||
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(6*time.Minute))
|
||||
}
|
||||
|
||||
func (c *AcmeClient) UseManualDns(domains []string) (*Resolve, error) {
|
||||
p := &manualDnsProvider{}
|
||||
if err := c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(6*time.Minute)); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: domains,
|
||||
Bundle: true,
|
||||
}
|
||||
|
||||
_, err := c.Client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Resolve, nil
|
||||
}
|
||||
|
||||
func (c *AcmeClient) UseHTTP() {
|
||||
|
||||
}
|
||||
@ -108,3 +138,29 @@ func (c *AcmeClient) GetSSL(domains []string) (certificate.Resource, error) {
|
||||
|
||||
return *certificates, nil
|
||||
}
|
||||
|
||||
type Resolve struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type manualDnsProvider struct {
|
||||
Resolve *Resolve
|
||||
}
|
||||
|
||||
func (p *manualDnsProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
p.Resolve = &Resolve{
|
||||
Key: fqdn,
|
||||
Value: value,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AcmeClient) GetDNSResolve() {
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,19 @@ export namespace WebSite {
|
||||
appinstall?: NewAppInstall;
|
||||
}
|
||||
|
||||
export interface WebSiteDTO extends CommonModel {
|
||||
primaryDomain: string;
|
||||
type: string;
|
||||
alias: string;
|
||||
remark: string;
|
||||
domains: WebSite.Domain[];
|
||||
appType: string;
|
||||
appInstallId?: number;
|
||||
webSiteGroupId: number;
|
||||
otherDomains: string;
|
||||
appinstall?: NewAppInstall;
|
||||
}
|
||||
|
||||
export interface NewAppInstall {
|
||||
name: string;
|
||||
appDetailId: number;
|
||||
@ -115,6 +128,19 @@ export namespace WebSite {
|
||||
startDate: string;
|
||||
}
|
||||
|
||||
export interface SSLCreate {
|
||||
domains: string[];
|
||||
provider: string;
|
||||
websiteId: number;
|
||||
acmeAccountId: number;
|
||||
dnsAccountId: number;
|
||||
}
|
||||
|
||||
export interface SSLApply {
|
||||
websiteId: number;
|
||||
SSLId: number;
|
||||
}
|
||||
|
||||
export interface AcmeAccount extends CommonModel {
|
||||
email: string;
|
||||
url: string;
|
||||
@ -123,4 +149,15 @@ export namespace WebSite {
|
||||
export interface AcmeAccountCreate {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface DNSResolveReq {
|
||||
domains: string[];
|
||||
acmeAccountId: number;
|
||||
}
|
||||
|
||||
export interface DNSResolve {
|
||||
key: string;
|
||||
value: string;
|
||||
type: string;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export const UpdateWebsite = (req: WebSite.WebSiteUpdateReq) => {
|
||||
};
|
||||
|
||||
export const GetWebsite = (id: number) => {
|
||||
return http.get<WebSite.WebSite>(`/websites/${id}`);
|
||||
return http.get<WebSite.WebSiteDTO>(`/websites/${id}`);
|
||||
};
|
||||
|
||||
export const DeleteWebsite = (req: WebSite.WebSiteDel) => {
|
||||
@ -59,11 +59,11 @@ export const UpdateNginxConfig = (req: WebSite.NginxConfigReq) => {
|
||||
};
|
||||
|
||||
export const SearchDnsAccount = (req: ReqPage) => {
|
||||
return http.post<ResPage<WebSite.DnsAccount>>(`/websites/dns`, req);
|
||||
return http.post<ResPage<WebSite.DnsAccount>>(`/websites/dns/search`, req);
|
||||
};
|
||||
|
||||
export const CreateDnsAccount = (req: WebSite.DnsAccountCreate) => {
|
||||
return http.post<any>(`/websites/dns/create`, req);
|
||||
return http.post<any>(`/websites/dns`, req);
|
||||
};
|
||||
|
||||
export const UpdateDnsAccount = (req: WebSite.DnsAccountUpdate) => {
|
||||
@ -74,10 +74,6 @@ export const DeleteDnsAccount = (id: number) => {
|
||||
return http.delete<any>(`/websites/dns/${id}`);
|
||||
};
|
||||
|
||||
export const SearchSSL = (req: ReqPage) => {
|
||||
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl`, req);
|
||||
};
|
||||
|
||||
export const SearchAcmeAccount = (req: ReqPage) => {
|
||||
return http.post<ResPage<WebSite.AcmeAccount>>(`/websites/acme/search`, req);
|
||||
};
|
||||
@ -89,3 +85,23 @@ export const CreateAcmeAccount = (req: WebSite.AcmeAccountCreate) => {
|
||||
export const DeleteAcmeAccount = (id: number) => {
|
||||
return http.delete<any>(`/websites/acme/${id}`);
|
||||
};
|
||||
|
||||
export const SearchSSL = (req: ReqPage) => {
|
||||
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl/search`, req);
|
||||
};
|
||||
|
||||
export const CreateSSL = (req: WebSite.SSLCreate) => {
|
||||
return http.post<WebSite.SSLCreate>(`/websites/ssl`, req);
|
||||
};
|
||||
|
||||
export const DeleteSSL = (id: number) => {
|
||||
return http.delete<any>(`/websites/ssl/${id}`);
|
||||
};
|
||||
|
||||
export const ApplySSL = (req: WebSite.SSLApply) => {
|
||||
return http.post<WebSite.SSLApply>(`/websites/ssl/apply`, req);
|
||||
};
|
||||
|
||||
export const GetDnsResolve = (req: WebSite.DNSResolveReq) => {
|
||||
return http.post<WebSite.DNSResolve>(`/websites/ssl/resolve`, req);
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ export const useDeleteData = <P = any, R = any>(
|
||||
if (!res) return reject(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: i18n.global.t('commons.msg.deleteSuccess'),
|
||||
message: i18n.global.t('commons.msg.operationSuccess'),
|
||||
});
|
||||
resolve(true);
|
||||
})
|
||||
|
@ -692,7 +692,7 @@ export default {
|
||||
perip: '单IP限制',
|
||||
peripHelper: '限制单个IP访问最大并发数',
|
||||
rate: '流量限制',
|
||||
rateHelper: '限制每个请求的流量上限(单位:KB)',
|
||||
rateHelper: '限制每个请求的流量上(单位:KB)',
|
||||
limtHelper: '启用流量控制',
|
||||
other: '其他',
|
||||
currentSSL: '当前证书',
|
||||
@ -707,5 +707,12 @@ export default {
|
||||
accountManage: '账户管理',
|
||||
email: '邮箱',
|
||||
addAccount: '新增账户',
|
||||
acmeAccount: 'Acme 账户',
|
||||
provider: '验证方式',
|
||||
dnsCommon: '手动解析',
|
||||
expireDate: '到期时间',
|
||||
brand: '品牌',
|
||||
deploySSL: '部署',
|
||||
deploySSLHelper: '确定部署证书?',
|
||||
},
|
||||
};
|
||||
|
@ -16,15 +16,15 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import LayoutContent from '@/layout/layout-content.vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import Basic from './basic/index.vue';
|
||||
import SSL from './ssl/index.vue';
|
||||
import router from '@/routers';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
type: String,
|
||||
default: '0',
|
||||
},
|
||||
tab: {
|
||||
type: String,
|
||||
@ -32,10 +32,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const id = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
|
||||
let id = ref(0);
|
||||
let index = ref('basic');
|
||||
|
||||
const changeTab = (index: string) => {
|
||||
@ -43,6 +40,7 @@ const changeTab = (index: string) => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
id.value = Number(props.id);
|
||||
if (props.tab !== index.value) {
|
||||
index.value = props.tab;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ const types = [
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.aliyun'),
|
||||
value: 'Aliyun',
|
||||
value: 'AliYun',
|
||||
},
|
||||
{
|
||||
label: 'CloudFlare',
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Account :id="id" v-if="index == '1'"></Account>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.applySSL')">
|
||||
<SSL v-if="index == '2'"></SSL>
|
||||
<SSL :id="id" v-if="index == '2'"></SSL>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<el-dialog v-model="open" :title="$t('commons.button.create')" width="30%" :before-close="handleClose">
|
||||
<el-form
|
||||
ref="sslForm"
|
||||
label-position="right"
|
||||
:model="ssl"
|
||||
label-width="150px"
|
||||
:rules="rules"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item :label="$t('website.acmeAccount')" prop="acmeAccountId">
|
||||
<el-select v-model="ssl.acmeAccountId">
|
||||
<el-option
|
||||
v-for="(acme, index) in acmeAccounts"
|
||||
:key="index"
|
||||
:label="acme.email"
|
||||
:value="acme.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.provider')" prop="provider">
|
||||
<el-radio-group v-model="ssl.provider">
|
||||
<el-radio label="dnsAccount">{{ $t('website.dnsAccount') }}</el-radio>
|
||||
<el-radio label="dnsCommon">{{ $t('website.dnsCommon') }}</el-radio>
|
||||
<el-radio label="http">HTTP</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.dnsAccount')" prop="dnsAccountId" v-if="ssl.provider === 'dnsAccount'">
|
||||
<el-select v-model="ssl.dnsAccountId">
|
||||
<el-option
|
||||
v-for="(dns, index) in dnsAccounts"
|
||||
:key="index"
|
||||
:label="dns.name + ' ( ' + dns.type + ' )'"
|
||||
:value="dns.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.domain')" prop="domains">
|
||||
<el-checkbox-group v-model="ssl.domains">
|
||||
<el-checkbox v-for="domain in domains" :key="domain.domain" :label="domain.domain"></el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div>
|
||||
<span>解析域名: {{ dnsResolve.key }}</span>
|
||||
<span>记录值: {{ dnsResolve.value }}</span>
|
||||
<span>类型: {{ dnsResolve.type }}</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(sslForm)" :loading="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { WebSite } from '@/api/interface/website';
|
||||
import { CreateSSL, GetDnsResolve, GetWebsite, SearchAcmeAccount, SearchDnsAccount } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessage, FormInstance } from 'element-plus';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const id = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
|
||||
let open = ref(false);
|
||||
let loading = ref(false);
|
||||
let dnsReq = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
let acmeReq = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
let dnsAccounts = ref<WebSite.DnsAccount[]>();
|
||||
let acmeAccounts = ref<WebSite.AcmeAccount[]>();
|
||||
let domains = ref<WebSite.Domain[]>([]);
|
||||
let sslForm = ref<FormInstance>();
|
||||
let rules = ref({
|
||||
acmeAccountId: [Rules.requiredSelectBusiness],
|
||||
dnsAccountId: [Rules.requiredSelectBusiness],
|
||||
provider: [Rules.requiredInput],
|
||||
domains: [Rules.requiredSelect],
|
||||
});
|
||||
let ssl = ref({
|
||||
domains: [],
|
||||
provider: 'dnsAccount',
|
||||
websiteId: 0,
|
||||
acmeAccountId: 0,
|
||||
dnsAccountId: 0,
|
||||
});
|
||||
let dnsResolve = ref<WebSite.DNSResolve>({
|
||||
key: '',
|
||||
value: '',
|
||||
type: '',
|
||||
});
|
||||
let hasResolve = ref(false);
|
||||
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
open.value = false;
|
||||
em('close', false);
|
||||
};
|
||||
const resetForm = () => {
|
||||
sslForm.value?.resetFields();
|
||||
ssl.value = {
|
||||
domains: [],
|
||||
provider: 'dnsAccount',
|
||||
websiteId: 0,
|
||||
acmeAccountId: 0,
|
||||
dnsAccountId: 0,
|
||||
};
|
||||
};
|
||||
|
||||
const acceptParams = () => {
|
||||
resetForm();
|
||||
ssl.value.websiteId = Number(id.value);
|
||||
getWebsite(id.value);
|
||||
getAcmeAccounts();
|
||||
getDnsAccounts();
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const getAcmeAccounts = async () => {
|
||||
const res = await SearchAcmeAccount(acmeReq);
|
||||
acmeAccounts.value = res.data.items || [];
|
||||
if (acmeAccounts.value.length > 0) {
|
||||
ssl.value.acmeAccountId = res.data.items[0].id;
|
||||
}
|
||||
};
|
||||
|
||||
const getDnsAccounts = async () => {
|
||||
const res = await SearchDnsAccount(dnsReq);
|
||||
dnsAccounts.value = res.data.items || [];
|
||||
if (dnsAccounts.value.length > 0) {
|
||||
ssl.value.dnsAccountId = res.data.items[0].id;
|
||||
}
|
||||
};
|
||||
|
||||
const getWebsite = async (id: number) => {
|
||||
domains.value = (await GetWebsite(id)).data.domains || [];
|
||||
};
|
||||
|
||||
const getDnsResolve = async (acmeAccountId: number, domains: string[]) => {
|
||||
hasResolve.value = false;
|
||||
const res = await GetDnsResolve({ acmeAccountId: acmeAccountId, domains: domains });
|
||||
if (res.data) {
|
||||
dnsResolve.value = res.data;
|
||||
hasResolve.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
if (ssl.value.provider != 'dnsCommon' || hasResolve.value) {
|
||||
loading.value = true;
|
||||
await CreateSSL(ssl.value);
|
||||
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
|
||||
loading.value = false;
|
||||
} else {
|
||||
getDnsResolve(ssl.value.acmeAccountId, ssl.value.domains);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -2,20 +2,51 @@
|
||||
<div>
|
||||
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain>{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="primary" plain @click="openSSL()">{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="primary" plain @click="openAccount()">{{ $t('website.accountManage') }}</el-button>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="name"></el-table-column>
|
||||
<el-table-column :label="$t('website.domain')" fix show-overflow-tooltip prop="domain"></el-table-column>
|
||||
<el-table-column :label="$t('website.brand')" fix show-overflow-tooltip prop="type"></el-table-column>
|
||||
<el-table-column
|
||||
prop="expireDate"
|
||||
:label="$t('website.expireDate')"
|
||||
:formatter="dateFromat"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
<Account ref="accountRef"></Account>
|
||||
<Create :id="id" ref="sslCreateRef"></Create>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { SearchSSL } from '@/api/modules/website';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { ApplySSL, DeleteSSL, SearchSSL } from '@/api/modules/website';
|
||||
import Account from './account/index.vue';
|
||||
import Create from './create/index.vue';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
import { WebSite } from '@/api/interface/website';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const id = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
@ -23,22 +54,65 @@ const paginationConfig = reactive({
|
||||
total: 0,
|
||||
});
|
||||
const accountRef = ref();
|
||||
const sslCreateRef = ref();
|
||||
let data = ref();
|
||||
let loading = ref(false);
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('website.deploySSL'),
|
||||
click: function (row: WebSite.WebSite) {
|
||||
applySSL(row.id);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('app.delete'),
|
||||
click: function (row: WebSite.WebSite) {
|
||||
deleteSSL(row.id);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const search = () => {
|
||||
const req = {
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
};
|
||||
SearchSSL(req).then((res) => {
|
||||
data.value = res.data.items;
|
||||
paginationConfig.total = res.data.total;
|
||||
});
|
||||
loading.value = true;
|
||||
SearchSSL(req)
|
||||
.then((res) => {
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const openAccount = () => {
|
||||
accountRef.value.acceptParams();
|
||||
};
|
||||
const openSSL = () => {
|
||||
sslCreateRef.value.acceptParams();
|
||||
};
|
||||
|
||||
const deleteSSL = async (id: number) => {
|
||||
loading.value = true;
|
||||
await useDeleteData(DeleteSSL, id, 'commons.msg.delete', false);
|
||||
loading.value = false;
|
||||
search();
|
||||
};
|
||||
|
||||
const applySSL = async (sslId: number) => {
|
||||
loading.value = true;
|
||||
const apply = {
|
||||
websiteId: Number(id.value),
|
||||
SSLId: sslId,
|
||||
};
|
||||
await useDeleteData(ApplySSL, apply, 'website.deploySSLHelper', false);
|
||||
loading.value = false;
|
||||
search();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
|
2
go.mod
2
go.mod
@ -56,12 +56,12 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.4 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -139,6 +139,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1eXdZyMw3F7uGA9qPn2J4+R8=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@ -482,6 +484,7 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
@ -655,6 +658,7 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@ -665,6 +669,7 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -1653,6 +1658,7 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
|
Loading…
x
Reference in New Issue
Block a user