From 6d2e372a07ea731e0c1015e7168f986281ea7e92 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 Date: Thu, 10 Nov 2022 17:44:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0acme=20util=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0DNS=E8=B4=A6=E5=8F=B7=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/entry.go | 1 + backend/app/api/v1/website_dns_account.go | 66 +++++ backend/app/dto/website_dns_account.go | 21 ++ backend/app/model/website_dns_account.go | 8 + backend/app/repo/entry.go | 1 + backend/app/repo/website_dns_account.go | 29 +++ backend/app/service/entry.go | 2 + backend/app/service/website.go | 2 +- backend/app/service/website_dns_account.go | 67 +++++ backend/app/service/website_utils.go | 24 +- backend/init/migration/migrate.go | 1 + backend/init/migration/migrations/init.go | 10 + backend/init/router/router.go | 1 + backend/router/entry.go | 1 + backend/router/ro_website_dns_account.go | 23 ++ backend/utils/nginx/private.key | 27 ++ backend/utils/ssl/acme.go | 90 +++++++ backend/utils/ssl/client.go | 110 ++++++++ frontend/src/api/interface/website.ts | 19 ++ frontend/src/api/modules/website.ts | 18 +- frontend/src/lang/modules/zh.ts | 10 + frontend/src/routers/modules/website.ts | 2 +- .../website/project/config/basic/index.vue | 5 +- .../views/website/project/config/index.vue | 30 ++- .../config/ssl/account/create/index.vue | 159 ++++++++++++ .../project/config/ssl/account/index.vue | 82 ++++++ .../project/config/ssl/current/index.vue | 5 + .../website/project/config/ssl/index.vue | 31 +++ frontend/src/views/website/project/index.vue | 5 +- go.mod | 14 +- go.sum | 235 +++++++++++++++++- 31 files changed, 1066 insertions(+), 33 deletions(-) create mode 100644 backend/app/api/v1/website_dns_account.go create mode 100644 backend/app/dto/website_dns_account.go create mode 100644 backend/app/model/website_dns_account.go create mode 100644 backend/app/repo/website_dns_account.go create mode 100644 backend/app/service/website_dns_account.go create mode 100644 backend/router/ro_website_dns_account.go create mode 100644 backend/utils/nginx/private.key create mode 100644 backend/utils/ssl/acme.go create mode 100644 backend/utils/ssl/client.go create mode 100644 frontend/src/views/website/project/config/ssl/account/create/index.vue create mode 100644 frontend/src/views/website/project/config/ssl/account/index.vue create mode 100644 frontend/src/views/website/project/config/ssl/current/index.vue create mode 100644 frontend/src/views/website/project/config/ssl/index.vue diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 6569abf19..852403e89 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -34,4 +34,5 @@ var ( commandService = service.ServiceGroupApp.CommandService websiteGroupService = service.ServiceGroupApp.WebsiteGroupService websiteService = service.ServiceGroupApp.WebsiteService + webSiteDnsAccountService = service.ServiceGroupApp.WebSiteDnsAccountService ) diff --git a/backend/app/api/v1/website_dns_account.go b/backend/app/api/v1/website_dns_account.go new file mode 100644 index 000000000..0e6d201b2 --- /dev/null +++ b/backend/app/api/v1/website_dns_account.go @@ -0,0 +1,66 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) PageWebsiteDnsAccount(c *gin.Context) { + var req dto.PageInfo + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + total, accounts, err := webSiteDnsAccountService.Page(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, dto.PageResult{ + Total: total, + Items: accounts, + }) +} + +func (b *BaseApi) CreateWebsiteDnsAccount(c *gin.Context) { + var req dto.WebsiteDnsAccountCreate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if _, err := webSiteDnsAccountService.Create(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) { + var req dto.WebsiteDnsAccountUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if _, err := webSiteDnsAccountService.Update(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) { + + id, err := helper.GetParamID(c) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := webSiteDnsAccountService.Delete(id); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/backend/app/dto/website_dns_account.go b/backend/app/dto/website_dns_account.go new file mode 100644 index 000000000..4e8b5dd6f --- /dev/null +++ b/backend/app/dto/website_dns_account.go @@ -0,0 +1,21 @@ +package dto + +import "github.com/1Panel-dev/1Panel/backend/app/model" + +type WebsiteDnsAccountDTO struct { + model.WebsiteDnsAccount + Authorization map[string]string `json:"authorization"` +} + +type WebsiteDnsAccountCreate struct { + Name string `json:"name" validate:"required"` + Type string `json:"type" validate:"required"` + Authorization map[string]string `json:"authorization" validate:"required"` +} + +type WebsiteDnsAccountUpdate struct { + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + Type string `json:"type" validate:"required"` + Authorization map[string]string `json:"authorization" validate:"required"` +} diff --git a/backend/app/model/website_dns_account.go b/backend/app/model/website_dns_account.go new file mode 100644 index 000000000..bd67729f2 --- /dev/null +++ b/backend/app/model/website_dns_account.go @@ -0,0 +1,8 @@ +package model + +type WebsiteDnsAccount struct { + BaseModel + Name string `gorm:"type:varchar(64);not null" json:"name"` + Type string `gorm:"type:varchar(64);not null" json:"type"` + Authorization string `gorm:"type:varchar(256);not null" json:"_"` +} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index 0b202ec41..50e643d74 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -23,6 +23,7 @@ type RepoGroup struct { WebSiteRepo WebSiteDomainRepo WebSiteGroupRepo + WebsiteDnsAccountRepo } var RepoGroupApp = new(RepoGroup) diff --git a/backend/app/repo/website_dns_account.go b/backend/app/repo/website_dns_account.go new file mode 100644 index 000000000..bd1d5ef49 --- /dev/null +++ b/backend/app/repo/website_dns_account.go @@ -0,0 +1,29 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/backend/app/model" +) + +type WebsiteDnsAccountRepo struct { +} + +func (w WebsiteDnsAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDnsAccount, error) { + var accounts []model.WebsiteDnsAccount + db := getDb(opts...).Model(&model.WebsiteDnsAccount{}) + count := int64(0) + db = db.Count(&count) + err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&accounts).Error + return count, accounts, err +} + +func (w WebsiteDnsAccountRepo) Create(account model.WebsiteDnsAccount) error { + return getDb().Create(&account).Error +} + +func (w WebsiteDnsAccountRepo) Save(account model.WebsiteDnsAccount) error { + return getDb().Save(&account).Error +} + +func (w WebsiteDnsAccountRepo) DeleteBy(opts ...DBOption) error { + return getDb(opts...).Delete(&model.WebsiteDnsAccount{}).Error +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index a3c069c8b..6258a2c8d 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -28,6 +28,7 @@ type ServiceGroup struct { OperationService WebsiteGroupService WebsiteService + WebSiteDnsAccountService } var ServiceGroupApp = new(ServiceGroup) @@ -62,4 +63,5 @@ var ( websiteRepo = repo.RepoGroupApp.WebSiteRepo websiteGroupRepo = repo.RepoGroupApp.WebSiteGroupRepo websiteDomainRepo = repo.RepoGroupApp.WebSiteDomainRepo + websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo ) diff --git a/backend/app/service/website.go b/backend/app/service/website.go index 14914e594..dbec8b349 100644 --- a/backend/app/service/website.go +++ b/backend/app/service/website.go @@ -232,7 +232,7 @@ func (w WebsiteService) UpdateNginxConfigByScope(req dto.NginxConfigReq) error { return err } if req.Operate == dto.ConfigDel { - return deleteNginxConfig(website, keys, req.Scope) + return deleteNginxConfig(website, keys) } return updateNginxConfig(website, getNginxParams(req.Params, keys), req.Scope) diff --git a/backend/app/service/website_dns_account.go b/backend/app/service/website_dns_account.go new file mode 100644 index 000000000..dded95865 --- /dev/null +++ b/backend/app/service/website_dns_account.go @@ -0,0 +1,67 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/app/model" + "gopkg.in/square/go-jose.v2/json" +) + +type WebSiteDnsAccountService struct { +} + +func (w WebSiteDnsAccountService) Page(search dto.PageInfo) (int64, []dto.WebsiteDnsAccountDTO, error) { + total, accounts, err := websiteDnsRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc")) + var accountDTOs []dto.WebsiteDnsAccountDTO + for _, account := range accounts { + auth := make(map[string]string) + _ = json.Unmarshal([]byte(account.Authorization), &auth) + accountDTOs = append(accountDTOs, dto.WebsiteDnsAccountDTO{ + WebsiteDnsAccount: account, + Authorization: auth, + }) + } + return total, accountDTOs, err +} + +func (w WebSiteDnsAccountService) Create(create dto.WebsiteDnsAccountCreate) (dto.WebsiteDnsAccountCreate, error) { + + authorization, err := json.Marshal(create.Authorization) + if err != nil { + return dto.WebsiteDnsAccountCreate{}, err + } + + if err := websiteDnsRepo.Create(model.WebsiteDnsAccount{ + Name: create.Name, + Type: create.Type, + Authorization: string(authorization), + }); err != nil { + return dto.WebsiteDnsAccountCreate{}, err + } + + return create, nil +} + +func (w WebSiteDnsAccountService) Update(update dto.WebsiteDnsAccountUpdate) (dto.WebsiteDnsAccountUpdate, error) { + + authorization, err := json.Marshal(update.Authorization) + if err != nil { + return dto.WebsiteDnsAccountUpdate{}, err + } + + if err := websiteDnsRepo.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 WebSiteDnsAccountService) Delete(id uint) error { + return websiteDnsRepo.DeleteBy(commonRepo.WithByID(id)) +} diff --git a/backend/app/service/website_utils.go b/backend/app/service/website_utils.go index b4ca2504b..f01e81242 100644 --- a/backend/app/service/website_utils.go +++ b/backend/app/service/website_utils.go @@ -57,7 +57,7 @@ func configDefaultNginx(website *model.WebSite, domains []model.WebSiteDomain) e return err } - nginxFileName := website.PrimaryDomain + ".conf" + nginxFileName := website.Alias + ".conf" configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName) nginxContent := string(nginx_conf.WebsiteDefault) @@ -111,7 +111,7 @@ func delNginxConfig(website model.WebSite) error { return err } - nginxFileName := website.PrimaryDomain + ".conf" + nginxFileName := website.Alias + ".conf" configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName) fileOp := files.NewFileOp() @@ -139,7 +139,7 @@ func nginxCheckAndReload(oldContent string, filePath string, containerName strin return nil } -func getNginxConfig(primaryDomain string) (dto.NginxConfig, error) { +func getNginxConfig(alias string) (dto.NginxConfig, error) { var nginxConfig dto.NginxConfig nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx")) if err != nil { @@ -150,7 +150,7 @@ func getNginxConfig(primaryDomain string) (dto.NginxConfig, error) { return nginxConfig, err } - configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", primaryDomain+".conf") + configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", alias+".conf") content, err := os.ReadFile(configPath) if err != nil { return nginxConfig, err @@ -167,7 +167,7 @@ func getNginxConfig(primaryDomain string) (dto.NginxConfig, error) { func addListenAndServerName(website model.WebSite, ports []int, domains []string) error { - nginxConfig, err := getNginxConfig(website.PrimaryDomain) + nginxConfig, err := getNginxConfig(website.Alias) if err != nil { return nil } @@ -187,7 +187,7 @@ func addListenAndServerName(website model.WebSite, ports []int, domains []string func deleteListenAndServerName(website model.WebSite, ports []int, domains []string) error { - nginxConfig, err := getNginxConfig(website.PrimaryDomain) + nginxConfig, err := getNginxConfig(website.Alias) if err != nil { return nil } @@ -207,7 +207,7 @@ func deleteListenAndServerName(website model.WebSite, ports []int, domains []str } func getNginxConfigByKeys(website model.WebSite, keys []string) ([]dto.NginxParam, error) { - nginxConfig, err := getNginxConfig(website.PrimaryDomain) + nginxConfig, err := getNginxConfig(website.Alias) if err != nil { return nil, err } @@ -233,7 +233,7 @@ func getNginxConfigByKeys(website model.WebSite, keys []string) ([]dto.NginxPara } func updateNginxConfig(website model.WebSite, params []dto.NginxParam, scope dto.NginxScope) error { - nginxConfig, err := getNginxConfig(website.PrimaryDomain) + nginxConfig, err := getNginxConfig(website.Alias) if err != nil { return err } @@ -270,8 +270,8 @@ func updateConfig(config *components.Config, scope dto.NginxScope) { } } -func deleteNginxConfig(website model.WebSite, keys []string, scope dto.NginxScope) error { - nginxConfig, err := getNginxConfig(website.PrimaryDomain) +func deleteNginxConfig(website model.WebSite, keys []string) error { + nginxConfig, err := getNginxConfig(website.Alias) if err != nil { return err } @@ -324,8 +324,8 @@ func getNginxParams(params interface{}, keys []string) []dto.NginxParam { var nginxParams []dto.NginxParam switch params.(type) { - case map[string]string: - return handleParamMap(params.(map[string]string), keys) + case map[string]interface{}: + return handleParamMap(toMapStr(params.(map[string]interface{})), keys) case []interface{}: if mArray, ok := params.([]interface{}); ok { diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 89fc76b44..c5d2701cd 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -18,6 +18,7 @@ func Init() { migrations.AddTableApp, migrations.AddTableImageRepo, migrations.AddTableWebsite, + migrations.AddTableDnsAccount, migrations.AddTableDatabaseMysql, }) if err := m.Migrate(); err != nil { diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 64f528760..1356da214 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -194,3 +194,13 @@ var AddTableWebsite = &gormigrate.Migration{ return nil }, } + +var AddTableDnsAccount = &gormigrate.Migration{ + ID: "20201009-add-table-dns", + Migrate: func(tx *gorm.DB) error { + if err := tx.AutoMigrate(&model.WebsiteDnsAccount{}); err != nil { + return err + } + return nil + }, +} diff --git a/backend/init/router/router.go b/backend/init/router/router.go index 176c6e8f8..3559a1513 100644 --- a/backend/init/router/router.go +++ b/backend/init/router/router.go @@ -81,6 +81,7 @@ func Routers() *gin.Engine { systemRouter.InitAppRouter(PrivateGroup) systemRouter.InitWebsiteRouter(PrivateGroup) systemRouter.InitWebsiteGroupRouter(PrivateGroup) + systemRouter.InitWebsiteDnsAccountRouter(PrivateGroup) systemRouter.InitDatabaseRouter(PrivateGroup) } diff --git a/backend/router/entry.go b/backend/router/entry.go index d05a3ae7e..a45f799fd 100644 --- a/backend/router/entry.go +++ b/backend/router/entry.go @@ -16,6 +16,7 @@ type RouterGroup struct { AppRouter WebsiteRouter WebsiteGroupRouter + WebsiteDnsAccountRouter DatabaseRouter } diff --git a/backend/router/ro_website_dns_account.go b/backend/router/ro_website_dns_account.go new file mode 100644 index 000000000..3130464a6 --- /dev/null +++ b/backend/router/ro_website_dns_account.go @@ -0,0 +1,23 @@ +package router + +import ( + v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1" + "github.com/1Panel-dev/1Panel/backend/middleware" + "github.com/gin-gonic/gin" +) + +type WebsiteDnsAccountRouter struct { +} + +func (a *WebsiteDnsAccountRouter) InitWebsiteDnsAccountRouter(Router *gin.RouterGroup) { + groupRouter := Router.Group("websites/dns") + groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) + + baseApi := v1.ApiGroupApp.BaseApi + { + groupRouter.POST("", baseApi.PageWebsiteDnsAccount) + groupRouter.POST("/create", baseApi.CreateWebsiteDnsAccount) + groupRouter.POST("/update", baseApi.UpdateWebsiteDnsAccount) + groupRouter.DELETE("/:id", baseApi.DeleteWebsiteDnsAccount) + } +} diff --git a/backend/utils/nginx/private.key b/backend/utils/nginx/private.key new file mode 100644 index 000000000..d6080982f --- /dev/null +++ b/backend/utils/nginx/private.key @@ -0,0 +1,27 @@ +-----BEGIN privateKey----- +MIIEowIBAAKCAQEA068zRCP9pn5YVkIQxSBqZAdYphzOqCvpGXnR4e3Ieh6C0MD8 +aF3//cIxrH8y20bUbJvPKEVxb9v8TGEVGeR94RC76fDHhy0AkGJfSa73ARe6kuiB +u4AWgJ8gm1TuREduIaEVLDjwrH3pubTNYgaVUuAT8Vo8dHYFxW/MMaiTE3c+vtId +iBYJtjI0MqJP/7YP77hI6N/4R7InBq9fR7YUZim6L8GR/zndn615srven9qOUVbX +3JuMZ8fk7tQ2FKrxBXKz3uq53BZ7xNNVmDqVCHu/rFVpT+UbPPiSW/+hWycPFJny +nDpTWrKGnYH/VR4ItOhoa55Kuob9zoEYH8hzUwIDAQABAoIBABImPFwERfQncwV6 +RpvQKq8G3jfn0mQi16qCglc/nuexg//H/Bwqqw8jvkSO51pbmUzykvFd+trfXqNh +04BT0eMhHytwpHrXhevbM1ZK9QyX7zw3SSA9XDCM9Cu9PWyHP08M05bmDuSLKyXr +9YwJCnZ/ldYkqXhGwjnqWvSLAokxkTU9ku1ZmCFlBOhAKj52sTUA5vYyt2JHIlZk +bn68jETVqs9pMehBTjv6btmQ+0OGmwOGWyBhU1WjVLxImyMHMD6Mqm5avoFyuCZa +y/SmG1jJPc+4k4+CilIBuh4tqEHESN8cP/pblkvitFAc8yjxoBAWNGPdAkvG8v2h +y/so1AECgYEA76Fh8SbQhEV0gCKvIkISp5ba51X+lPD/VzPS8195B5QWLQW9a6R/ +flbYwBLBjong8EAeoo4QWzQMH1DCF1XyuQVVjqjQKHxr4atvyLXloEKdippSMubQ +Ebk91Ih43vLmC0QpqmFwLDr6BFP7QofS88zuM/L0qRKwD0BAr453PgECgYEA4iUa +JiRJ9tbJt6Yz2dYyqY6THtL/YqfRFLtFiGt+eTw7wFL5Yhu1kO6AT4/begXxqlzJ +SUYj4DLWaEh+fMwCGqwDxP0CbXbBQ3D1AiJueENbsnPPw0b0D0aWD22S8xdcXgEi +6vD1Ck8C/XGECXvXohAxxB41WxfnwQPZ01yRWVMCgYAFRcXD6pi38FdJ4nl8BXwn +l9glMu0xFcRohMlldXpSQl3ii7fJQItVmk+WmlML8dizMJX7+ag+eXyyy7IZlViL +3aQuSWSbH3G4O29wOSBeUFjrDc41NILrgOntXTtbnHiXUt6f0xkGwB10LuzeX+Ky +XpX3KqdbeP/Kth02P6o8AQKBgQCs94/OiIcCAzp5+udBwDEzutcQBnZLMOwcHTiL +pRwxAj75VP1laqgu2BzPvcoyxIUYtqtGHh2mh3uye3AzZ55cZCDl9FZ8/w3G1Yiw +P0GbPnnOPgI06/oUDPsNhdOVltKRG/FnHTFu1wwEaWSCFHTTgetug2Rv7hLWcGmU +3gJV/QKBgBibHHl6ZcMeNSTWz5LMxiLMmEg9+/myVrVTCP7/CpQuGqi2c43VuPJC +iXxoqPmS8UEPL7wcezPDI1lhxswVzTSn+Xp2vFY0Z7lJxqY2dbaJMwtju21l8WfX +KryKfGhJVvh60GFiYpw5IGgrGV9kqEfaopJswQ53jUCw4Pmo8e8r +-----END privateKey----- diff --git a/backend/utils/ssl/acme.go b/backend/utils/ssl/acme.go new file mode 100644 index 000000000..06ddc4d02 --- /dev/null +++ b/backend/utils/ssl/acme.go @@ -0,0 +1,90 @@ +package ssl + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/registration" +) + +func GetPrivateKey(priKey *rsa.PrivateKey) []byte { + derStream := x509.MarshalPKCS1PrivateKey(priKey) + block := &pem.Block{ + Type: "privateKey", + Bytes: derStream, + } + return pem.EncodeToMemory(block) +} + +func NewRegisterClient(email string) (*AcmeClient, error) { + + priKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + myUser := &AcmeUser{ + Email: email, + key: priKey, + } + config := newConfig(myUser) + client, err := lego.NewClient(config) + if err != nil { + return nil, err + } + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + return nil, err + } + myUser.Registration = reg + + acmeClient := &AcmeClient{ + User: myUser, + Client: client, + Config: config, + } + + return acmeClient, nil +} + +func NewPrivateKeyClient(email string, privateKey string) (*AcmeClient, error) { + + block, _ := pem.Decode([]byte(privateKey)) + priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + myUser := &AcmeUser{ + Email: email, + key: priKey, + } + config := newConfig(myUser) + client, err := lego.NewClient(config) + if err != nil { + return nil, err + } + reg, err := client.Registration.ResolveAccountByKey() + if err != nil { + return nil, err + } + myUser.Registration = reg + + acmeClient := &AcmeClient{ + User: myUser, + Client: client, + Config: config, + } + + return acmeClient, nil +} + +func newConfig(user *AcmeUser) *lego.Config { + config := lego.NewConfig(user) + config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory" + config.UserAgent = "acm_go/0.0.1" + config.Certificate.KeyType = certcrypto.RSA2048 + + return config +} diff --git a/backend/utils/ssl/client.go b/backend/utils/ssl/client.go new file mode 100644 index 000000000..f2b6b5cd9 --- /dev/null +++ b/backend/utils/ssl/client.go @@ -0,0 +1,110 @@ +package ssl + +import ( + "crypto" + "encoding/json" + "github.com/go-acme/lego/v4/certificate" + "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/dnspod" + "github.com/go-acme/lego/v4/registration" + "github.com/pkg/errors" + "time" +) + +type AcmeUser struct { + Email string + Registration *registration.Resource + key crypto.PrivateKey +} + +func (u *AcmeUser) GetEmail() string { + return u.Email +} + +func (u *AcmeUser) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey { + return u.key +} + +type AcmeClient struct { + Config *lego.Config + Client *lego.Client + User *AcmeUser +} + +func NewAcmeClient(email, privateKey string) (*AcmeClient, error) { + if email == "" { + return nil, errors.New("email can not blank") + } + if privateKey == "" { + client, err := NewRegisterClient(email) + if err != nil { + return nil, err + } + return client, nil + } else { + client, err := NewPrivateKeyClient(email, privateKey) + if err != nil { + return nil, err + } + return client, nil + } +} + +type DnsType string + +const ( + DnsPod DnsType = "dnsPod" +) + +type DNSParam struct { + ID string `json:"id"` + Token string `json:"token"` + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` + Email string `json:"email"` + APIkey string `json:"APIkey"` +} + +func (c *AcmeClient) UseDns(dnsType DnsType, params string) error { + + var param DNSParam + if err := json.Unmarshal([]byte(params), ¶m); err != nil { + return err + } + + var p challenge.Provider + var err error + if dnsType == DnsPod { + dnsPodConfig := dnspod.NewDefaultConfig() + dnsPodConfig.LoginToken = param.ID + "," + param.Token + p, err = dnspod.NewDNSProviderConfig(dnsPodConfig) + if err != nil { + return err + } + } + + return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(6*time.Minute)) +} +func (c *AcmeClient) UseHTTP() { + +} + +func (c *AcmeClient) GetSSL(domains []string) (certificate.Resource, error) { + + request := certificate.ObtainRequest{ + Domains: domains, + Bundle: true, + } + + certificates, err := c.Client.Certificate.Obtain(request) + if err != nil { + return certificate.Resource{}, err + } + + return *certificates, nil +} diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index 822ce4f12..2ce68f08e 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -84,4 +84,23 @@ export namespace WebSite { isRepeatKey: string; params: string[]; } + + export interface DnsAccount extends CommonModel { + name: string; + type: string; + authorization: Object; + } + + export interface DnsAccountCreate { + name: string; + type: string; + authorization: Object; + } + + export interface DnsAccountUpdate { + id: number; + name: string; + type: string; + authorization: Object; + } } diff --git a/frontend/src/api/modules/website.ts b/frontend/src/api/modules/website.ts index e5bd1f06d..739b75916 100644 --- a/frontend/src/api/modules/website.ts +++ b/frontend/src/api/modules/website.ts @@ -1,5 +1,5 @@ import http from '@/api'; -import { ResPage } from '../interface'; +import { ReqPage, ResPage } from '../interface'; import { WebSite } from '../interface/website'; export const SearchWebSites = (req: WebSite.WebSiteSearch) => { @@ -57,3 +57,19 @@ export const GetNginxConfig = (req: WebSite.NginxConfigReq) => { export const UpdateNginxConfig = (req: WebSite.NginxConfigReq) => { return http.post(`/websites/config/update`, req); }; + +export const SearchDnsAccount = (req: ReqPage) => { + return http.post>(`/websites/dns`, req); +}; + +export const CreateDnsAccount = (req: WebSite.DnsAccountCreate) => { + return http.post(`/websites/dns/create`, req); +}; + +export const UpdateDnsAccount = (req: WebSite.DnsAccountUpdate) => { + return http.post(`/websites/dns/update`, req); +}; + +export const DeleteDnsAccount = (id: number) => { + return http.delete(`/websites/dns/${id}`); +}; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index a03f45fad..66847a0e0 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -694,5 +694,15 @@ export default { rate: '流量限制', rateHelper: '限制每个请求的流量上限(单位:KB)', limtHelper: '启用流量控制', + other: '其他', + currentSSL: '当前证书', + dnsAccount: 'DNS账号', + applySSL: '证书申请', + SSLList: '证书列表', + createDnsAccount: 'DNS账号', + aliyun: '阿里云', + manual: '手动解析', + key: '密钥', + check: '查看', }, }; diff --git a/frontend/src/routers/modules/website.ts b/frontend/src/routers/modules/website.ts index d4b5130a3..5bba5e388 100644 --- a/frontend/src/routers/modules/website.ts +++ b/frontend/src/routers/modules/website.ts @@ -19,7 +19,7 @@ const webSiteRouter = { }, }, { - path: '/websites/:id/config', + path: '/websites/:id/config/:tab', name: 'WebsiteConfig', component: () => import('@/views/website/project/config/index.vue'), hidden: true, diff --git a/frontend/src/views/website/project/config/basic/index.vue b/frontend/src/views/website/project/config/basic/index.vue index 0556149f2..67fa15bc2 100644 --- a/frontend/src/views/website/project/config/basic/index.vue +++ b/frontend/src/views/website/project/config/basic/index.vue @@ -9,9 +9,8 @@ - Role - - + + diff --git a/frontend/src/views/website/project/config/index.vue b/frontend/src/views/website/project/config/index.vue index f75ef3aaa..0baa60621 100644 --- a/frontend/src/views/website/project/config/index.vue +++ b/frontend/src/views/website/project/config/index.vue @@ -1,9 +1,13 @@