From a225d2d79ad57427767fd8da41facfe0a1b78ead Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Thu, 8 Aug 2024 18:26:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=BD=91=E7=AB=99?= =?UTF-8?q?=E5=9F=9F=E5=90=8D=E6=B7=BB=E5=8A=A0=20(#6069)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/app/dto/request/website.go | 16 +- agent/app/dto/response/website.go | 3 +- agent/app/model/website.go | 2 - agent/app/model/website_domain.go | 1 + agent/app/service/website.go | 74 +++++----- agent/app/service/website_utils.go | 124 ++++++++-------- agent/init/migration/migrate.go | 1 + agent/init/migration/migrations/init.go | 8 + frontend/src/api/interface/website.ts | 13 +- frontend/src/lang/modules/en.ts | 2 + frontend/src/lang/modules/tw.ts | 2 + frontend/src/lang/modules/zh.ts | 2 + .../config/basic/domain/create/index.vue | 52 +++---- .../website/config/basic/https/index.vue | 13 +- .../views/website/website/create/index.vue | 47 ++---- .../website/website/domain-create/index.vue | 138 ++++++++++++++++++ 16 files changed, 313 insertions(+), 185 deletions(-) create mode 100644 frontend/src/views/website/website/domain-create/index.vue diff --git a/agent/app/dto/request/website.go b/agent/app/dto/request/website.go index b85d53515..31b90160b 100644 --- a/agent/app/dto/request/website.go +++ b/agent/app/dto/request/website.go @@ -13,15 +13,15 @@ type WebsiteSearch struct { } type WebsiteCreate struct { - PrimaryDomain string `json:"primaryDomain" validate:"required"` Type string `json:"type" validate:"required"` Alias string `json:"alias" validate:"required"` Remark string `json:"remark"` - OtherDomains string `json:"otherDomains"` Proxy string `json:"proxy"` WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"` IPV6 bool `json:"IPV6"` + Domains []WebsiteDomain `json:"domains"` + AppType string `json:"appType" validate:"oneof=new installed"` AppInstall NewAppInstall `json:"appInstall"` AppID uint `json:"appID"` @@ -123,8 +123,14 @@ type WebsiteGroupUpdate struct { } type WebsiteDomainCreate struct { - WebsiteID uint `json:"websiteID" validate:"required"` - Domains string `json:"domains" validate:"required"` + WebsiteID uint `json:"websiteID" validate:"required"` + Domains []WebsiteDomain `json:"domains" validate:"required"` +} + +type WebsiteDomain struct { + Domain string `json:"domain" validate:"required"` + Port int `json:"port"` + SSL bool `json:"SSL"` } type WebsiteDomainDelete struct { @@ -145,7 +151,7 @@ type WebsiteHTTPSOp struct { SSLProtocol []string `json:"SSLProtocol"` Algorithm string `json:"algorithm"` Hsts bool `json:"hsts"` - HttpsPort int `json:"httpsPort"` + HttpsPorts []int `json:"httpsPorts"` } type WebsiteNginxUpdate struct { diff --git a/agent/app/dto/response/website.go b/agent/app/dto/response/website.go index a61a89462..50d1e5330 100644 --- a/agent/app/dto/response/website.go +++ b/agent/app/dto/response/website.go @@ -59,7 +59,8 @@ type WebsiteHTTPS struct { SSLProtocol []string `json:"SSLProtocol"` Algorithm string `json:"algorithm"` Hsts bool `json:"hsts"` - HttpsPort int `json:"httpsPort"` + HttpsPorts []int `json:"httpsPorts"` + HttpsPort string `json:"httpsPort"` } type WebsiteLog struct { diff --git a/agent/app/model/website.go b/agent/app/model/website.go index 1b49bb2ac..15748739c 100644 --- a/agent/app/model/website.go +++ b/agent/app/model/website.go @@ -28,8 +28,6 @@ type Website struct { AppInstallID uint `gorm:"type:integer" json:"appInstallId"` FtpID uint `gorm:"type:integer" json:"ftpId"` - HttpsPort int `json:"httpsPort"` - User string `gorm:"type:varchar;" json:"user"` Group string `gorm:"type:varchar;" json:"group"` diff --git a/agent/app/model/website_domain.go b/agent/app/model/website_domain.go index e6fb77e93..222a122e6 100644 --- a/agent/app/model/website_domain.go +++ b/agent/app/model/website_domain.go @@ -4,6 +4,7 @@ type WebsiteDomain struct { BaseModel WebsiteID uint `gorm:"column:website_id;type:varchar(64);not null;" json:"websiteId"` Domain string `gorm:"type:varchar(256);not null" json:"domain"` + SSL bool `json:"SSL"` Port int `gorm:"type:integer" json:"port"` } diff --git a/agent/app/service/website.go b/agent/app/service/website.go index e3bc9fb2e..a490e0702 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -225,28 +225,21 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) return err } defaultHttpPort := nginxInstall.HttpPort - defaultHttpsPort := nginxInstall.HttpsPort - var ( - otherDomains []model.WebsiteDomain - domains []model.WebsiteDomain + domains []model.WebsiteDomain ) - domains, _, _, err = getWebsiteDomains(create.PrimaryDomain, defaultHttpPort, 0) + domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, 0) if err != nil { return err } - otherDomains, _, _, err = getWebsiteDomains(create.OtherDomains, defaultHttpPort, 0) - if err != nil { - return err - } - domains = append(domains, otherDomains...) - if len(domains) == 1 && domains[0].Port != defaultHttpPort { - defaultHttpsPort = domains[0].Port + primaryDomain := domains[0].Domain + if domains[0].Port != defaultHttpPort { + primaryDomain = fmt.Sprintf("%s:%v", domains[0].Domain, domains[0].Port) } defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate) website := &model.Website{ - PrimaryDomain: create.PrimaryDomain, + PrimaryDomain: primaryDomain, Type: create.Type, Alias: alias, Remark: create.Remark, @@ -259,7 +252,6 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) AccessLog: true, ErrorLog: true, IPV6: create.IPV6, - HttpsPort: defaultHttpsPort, } var ( @@ -267,7 +259,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) runtime *model.Runtime ) - createTask, err := task.NewTaskWithOps(create.PrimaryDomain, task.TaskCreate, task.TaskScopeWebsite, create.TaskID, 0) + createTask, err := task.NewTaskWithOps(primaryDomain, task.TaskCreate, task.TaskScopeWebsite, create.TaskID, 0) if err != nil { return err } @@ -464,7 +456,6 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) SSLProtocol: []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}, Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED", Hsts: true, - HttpsPort: website.HttpsPort, } if err = applySSL(website, *websiteModel, appSSLReq); err != nil { return err @@ -607,7 +598,6 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) var ( domainModels []model.WebsiteDomain addPorts []int - addDomains []string ) httpPort, _, err := getAppInstallPort(constant.AppOpenresty) if err != nil { @@ -618,7 +608,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) return nil, err } - domainModels, addPorts, addDomains, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID) + domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID) if err != nil { return nil, err } @@ -626,7 +616,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) _ = OperateFirewallPort(nil, addPorts) }() - if err := addListenAndServerName(website, addPorts, addDomains); err != nil { + if err = addListenAndServerName(website, domainModels); err != nil { return nil, err } @@ -868,8 +858,22 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, if err != nil { return response.WebsiteHTTPS{}, err } - var res response.WebsiteHTTPS - res.HttpsPort = website.HttpsPort + var ( + res response.WebsiteHTTPS + httpsPorts []string + ) + websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId)) + for _, domain := range websiteDomains { + if domain.SSL { + httpsPorts = append(httpsPorts, strconv.Itoa(domain.Port)) + } + } + if len(httpsPorts) == 0 { + nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty) + res.HttpsPort = strconv.Itoa(nginxInstall.HttpsPort) + } else { + res.HttpsPort = strings.Join(httpsPorts, ",") + } if website.WebsiteSSLID == 0 { res.Enable = false return res, nil @@ -925,17 +929,17 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH if !req.Enable { website.Protocol = constant.ProtocolHTTP website.WebsiteSSLID = 0 - httpsPort := website.HttpsPort - if httpsPort == 0 { - _, httpsPort, err = getAppInstallPort(constant.AppOpenresty) - if err != nil { - return nil, err - } - } - httpsPortStr := strconv.Itoa(httpsPort) - if err := deleteListenAndServerName(website, []string{httpsPortStr, "[::]:" + httpsPortStr}, []string{}); err != nil { - return nil, err - } + //httpsPort := req.HttpsPort + //if len(httpsPort) == 0 { + // _, httpsPort, err = getAppInstallPort(constant.AppOpenresty) + // if err != nil { + // return nil, err + // } + //} + //httpsPortStr := strconv.Itoa(httpsPort) + //if err = deleteListenAndServerName(website, []string{httpsPortStr, "[::]:" + httpsPortStr}, []string{}); err != nil { + // return nil, err + //} nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil) nginxParams = append(nginxParams, dto.NginxParam{ @@ -1035,18 +1039,18 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH } website.Protocol = constant.ProtocolHTTPS - if err := applySSL(&website, websiteSSL, req); err != nil { + if err = applySSL(&website, websiteSSL, req); err != nil { return nil, err } website.HttpConfig = req.HttpConfig if websiteSSL.ID == 0 { - if err := websiteSSLRepo.Create(ctx, &websiteSSL); err != nil { + if err = websiteSSLRepo.Create(ctx, &websiteSSL); err != nil { return nil, err } website.WebsiteSSLID = websiteSSL.ID } - if err := websiteRepo.Save(ctx, &website); err != nil { + if err = websiteRepo.Save(ctx, &website); err != nil { return nil, err } return &res, nil diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go index 83bf0c245..e9a85fce1 100644 --- a/agent/app/service/website_utils.go +++ b/agent/app/service/website_utils.go @@ -7,7 +7,6 @@ import ( "os" "path" "path/filepath" - "reflect" "strconv" "strings" "time" @@ -35,40 +34,6 @@ import ( "gorm.io/gorm" ) -func getDomain(domainStr string, defaultPort int) (model.WebsiteDomain, error) { - var ( - err error - domain = model.WebsiteDomain{} - portN int - ) - domainArray := strings.Split(domainStr, ":") - if len(domainArray) == 1 { - domain.Domain, err = handleChineseDomain(domainArray[0]) - if err != nil { - return domain, err - } - domain.Port = defaultPort - return domain, nil - } - if len(domainArray) > 1 { - domain.Domain, err = handleChineseDomain(domainArray[0]) - if err != nil { - return domain, err - } - portStr := domainArray[1] - portN, err = strconv.Atoi(portStr) - if err != nil { - return domain, buserr.WithName("ErrTypePort", portStr) - } - if portN <= 0 || portN > 65535 { - return domain, buserr.New("ErrTypePortRange") - } - domain.Port = portN - return domain, nil - } - return domain, nil -} - func handleChineseDomain(domain string) (string, error) { if common.ContainsChinese(domain) { return common.PunycodeEncode(domain) @@ -481,7 +446,7 @@ func delWafConfig(website model.Website, force bool) error { return nil } -func addListenAndServerName(website model.Website, ports []int, domains []string) error { +func addListenAndServerName(website model.Website, domains []model.WebsiteDomain) error { nginxFull, err := getNginxFull(&website) if err != nil { return nil @@ -489,16 +454,20 @@ func addListenAndServerName(website model.Website, ports []int, domains []string nginxConfig := nginxFull.SiteConfig config := nginxFull.SiteConfig.Config server := config.FindServers()[0] - for _, port := range ports { - server.AddListen(strconv.Itoa(port), false) - if website.IPV6 { - server.UpdateListen("[::]:"+strconv.Itoa(port), false) - } - } + for _, domain := range domains { - server.AddServerName(domain) + var params []string + if website.Protocol == constant.ProtocolHTTPS && domain.SSL { + params = append(params, "ssl", "http2") + } + server.AddListen(strconv.Itoa(domain.Port), false, params...) + if website.IPV6 { + server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false, params...) + } + server.UpdateServerName([]string{domain.Domain}) } - if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { + + if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil { return err } return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName) @@ -568,6 +537,24 @@ func createPemFile(website model.Website, websiteSSL model.WebsiteSSL) error { return nil } +func getHttpsPort(website *model.Website) ([]int, error) { + websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID)) + var httpsPorts []int + for _, domain := range websiteDomains { + if domain.SSL { + httpsPorts = append(httpsPorts, domain.Port) + } + } + if len(httpsPorts) == 0 { + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return nil, err + } + httpsPorts = append(httpsPorts, nginxInstall.HttpsPort) + } + return httpsPorts, nil +} + func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.WebsiteHTTPSOp) error { nginxFull, err := getNginxFull(website) if err != nil { @@ -587,17 +574,18 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W server := config.FindServers()[0] httpPort := strconv.Itoa(nginxFull.Install.HttpPort) - httpsPort := nginxFull.Install.HttpsPort - if req.HttpsPort > 0 { - httpsPort = req.HttpsPort + httpsPort, err := getHttpsPort(website) + if err != nil { + return err } - website.HttpsPort = httpsPort httpPortIPV6 := "[::]:" + httpPort - httpsPortIPV6 := "[::]:" + strconv.Itoa(httpsPort) - server.UpdateListen(strconv.Itoa(httpsPort), website.DefaultServer, "ssl", "http2") - if website.IPV6 { - server.UpdateListen(httpsPortIPV6, website.DefaultServer, "ssl", "http2") + for _, port := range httpsPort { + httpsPortIPV6 := "[::]:" + strconv.Itoa(port) + server.UpdateListen(strconv.Itoa(port), website.DefaultServer, "ssl", "http2") + if website.IPV6 { + server.UpdateListen(httpsPortIPV6, website.DefaultServer, "ssl", "http2") + } } switch req.HttpConfig { @@ -911,30 +899,36 @@ func changeServiceName(newComposeContent, newServiceName string) (composeByte [] return yaml.Marshal(composeMap) } -func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) { +func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) { var ( - ports = make(map[int]struct{}) + ports = make(map[int]struct{}) + existPort = make(map[int]struct{}) ) - domainArray := strings.Split(domains, "\n") - for _, domain := range domainArray { - if domain == "" { + existDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID)) + for _, domain := range existDomains { + existPort[domain.Port] = struct{}{} + } + for _, domain := range domains { + if domain.Domain == "" { continue } - if !common.IsValidDomain(domain) { - err = buserr.WithName("ErrDomainFormat", domain) + if !common.IsValidDomain(domain.Domain) { + err = buserr.WithName("ErrDomainFormat", domain.Domain) return } var domainModel model.WebsiteDomain - domainModel, err = getDomain(domain, defaultPort) + domainModel.Domain, err = handleChineseDomain(domain.Domain) if err != nil { return } - if reflect.DeepEqual(domainModel, model.WebsiteDomain{}) { - continue + domainModel.Port = domain.Port + if domain.Port == 0 { + domain.Port = defaultPort } + domainModel.SSL = domain.SSL domainModel.WebsiteID = websiteID domainModels = append(domainModels, domainModel) - if domainModel.Port != defaultPort { + if _, ok := existPort[domainModel.Port]; !ok { ports[domainModel.Port] = struct{}{} } if exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domainModel.Domain), websiteDomainRepo.WithWebsiteId(websiteID)); exist.ID == 0 { @@ -950,6 +944,10 @@ func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainM } for port := range ports { + if port == defaultPort { + addPorts = append(addPorts, port) + continue + } if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) == 0 { errMap := make(map[string]interface{}) errMap["port"] = port diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index cd8732407..51bec255e 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -19,6 +19,7 @@ func Init() { migrations.InitPHPExtensions, migrations.AddTask, migrations.UpdateWebsite, + migrations.UpdateWebsiteDomain, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index a029c0e2e..91f362a5c 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -296,3 +296,11 @@ var UpdateWebsite = &gormigrate.Migration{ &model.Website{}) }, } + +var UpdateWebsiteDomain = &gormigrate.Migration{ + ID: "20240808-update-website-domain", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate( + &model.WebsiteDomain{}) + }, +} diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index b5e902ee0..408330d09 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -67,14 +67,12 @@ export namespace Website { } export interface WebSiteCreateReq { - primaryDomain: string; type: string; alias: string; remark: string; appType: string; appInstallId: number; webSiteGroupId: number; - otherDomains: string; proxy: string; proxyType: string; ftpUser: string; @@ -88,6 +86,7 @@ export namespace Website { dbFormat?: string; dbUser?: string; dbHost?: string; + domains: SubDomain[]; } export interface WebSiteUpdateReq { @@ -128,7 +127,13 @@ export namespace Website { export interface DomainCreate { websiteID: number; - domains: string; + domains: SubDomain[]; + } + + interface SubDomain { + domain: string; + port: number; + ssl: boolean; } export interface DomainDelete { @@ -288,7 +293,7 @@ export namespace Website { SSLProtocol: string[]; algorithm: string; hsts: boolean; - httpsPort: number; + httpsPort?: string; } export interface CheckReq { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 7f8b81cf0..f6e1a7e50 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -2130,6 +2130,8 @@ const message = { "When the reverse proxy backend is HTTPS, you might need to set the origin SNI. Please refer to the CDN service provider's documentation for details.", createDb: 'Create Database', enableSSLHelper: 'Failure to enable will not affect the creation of the website', + batchAdd: 'Batch Add Domains', + generateDomain: 'Generate', }, php: { short_open_tag: 'Short tag support', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index fad659d0b..e602d94a0 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1980,6 +1980,8 @@ const message = { sniHelper: '反代後端為 https 的時候可能需要設置回源 SNI,具體需要看 CDN 服務商文檔', createDb: '建立資料庫', enableSSLHelper: '開啟失敗不會影響網站創建', + batchAdd: '批量添加域名', + generateDomain: '生成', }, php: { short_open_tag: '短標簽支持', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 2bad460fc..e37c6b58a 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1982,6 +1982,8 @@ const message = { sniHelper: '反代后端为 https 的时候可能需要设置回源 SNI,具体需要看 CDN 服务商文档', createDb: '创建数据库', enableSSLHelper: '开启失败不会影响网站创建', + batchAdd: '批量添加域名', + generateDomain: '生成', }, php: { short_open_tag: '短标签支持', diff --git a/frontend/src/views/website/website/config/basic/domain/create/index.vue b/frontend/src/views/website/website/config/basic/domain/create/index.vue index b352a5be3..b5d095f0a 100644 --- a/frontend/src/views/website/website/config/basic/domain/create/index.vue +++ b/frontend/src/views/website/website/config/basic/domain/create/index.vue @@ -1,30 +1,8 @@