feat: 域名支持批量添加 (#2774)

Refs https://github.com/1Panel-dev/1Panel/issues/2633
pull/2780/head
zhengkunwang 1 year ago committed by GitHub
parent 082032e15f
commit eebef06c77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -111,9 +111,8 @@ type WebsiteGroupUpdate struct {
}
type WebsiteDomainCreate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Port int `json:"port" validate:"required"`
Domain string `json:"domain" validate:"required"`
WebsiteID uint `json:"websiteID" validate:"required"`
Domains string `json:"domains" validate:"required"`
}
type WebsiteDomainDelete struct {

@ -19,7 +19,6 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/spf13/afero"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
@ -59,7 +58,7 @@ type IWebsiteService interface {
UpdateWebsite(req request.WebsiteUpdate) error
DeleteWebsite(req request.WebsiteDelete) error
GetWebsite(id uint) (response.WebsiteDTO, error)
CreateWebsiteDomain(create request.WebsiteDomainCreate) (model.WebsiteDomain, error)
CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error)
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
DeleteWebsiteDomain(domainId uint) error
@ -188,56 +187,18 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
defaultHttpPort := nginxInstall.HttpPort
var (
domains []model.WebsiteDomain
ports = make(map[int]struct{})
otherDomains []model.WebsiteDomain
domains []model.WebsiteDomain
)
domainArray := strings.Split(create.OtherDomains, "\n")
domainArray = append(domainArray, create.PrimaryDomain)
for _, domain := range domainArray {
if domain == "" {
continue
}
domainModel, err := getDomain(domain, defaultHttpPort)
if err != nil {
return err
}
if reflect.DeepEqual(domainModel, model.WebsiteDomain{}) {
continue
}
domains = append(domains, domainModel)
if domainModel.Port != defaultHttpPort {
ports[domainModel.Port] = struct{}{}
}
}
for _, domain := range domains {
if exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domain.Domain), websiteDomainRepo.WithPort(domain.Port)); exist.ID > 0 {
website, _ := websiteRepo.GetFirst(commonRepo.WithByID(exist.WebsiteID))
return buserr.WithName(constant.ErrDomainIsUsed, website.PrimaryDomain)
}
domains, _, _, err = getWebsiteDomains(create.PrimaryDomain, defaultHttpPort, 0)
if err != nil {
return err
}
for port := range ports {
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) == 0 {
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
if appInstall.ID > 0 {
errMap["type"] = i18n.GetMsgByKey("TYPE_APP")
errMap["name"] = appInstall.Name
return buserr.WithMap("ErrPortExist", errMap, nil)
}
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithPort(port))
if runtime != nil {
errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME")
errMap["name"] = runtime.Name
return buserr.WithMap("ErrPortExist", errMap, nil)
}
if common.ScanPort(port) {
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
}
}
otherDomains, _, _, err = getWebsiteDomains(create.OtherDomains, defaultHttpPort, 0)
if err != nil {
return err
}
domains = append(domains, otherDomains...)
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
website := &model.Website{
@ -487,59 +448,34 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
return nil
}
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) (model.WebsiteDomain, error) {
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) {
var (
domainModel model.WebsiteDomain
ports []int
domains []string
domainModels []model.WebsiteDomain
addPorts []int
addDomains []string
)
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return domainModel, err
}
if create.Port != httpPort {
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(create.Port)); len(existPorts) == 0 {
if existAppInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(create.Port)); existAppInstall.ID > 0 {
return domainModel, buserr.WithMap(constant.ErrPortInOtherApp, map[string]interface{}{"port": create.Port, "apps": existAppInstall.App.Name}, nil)
}
if common.ScanPort(create.Port) {
return domainModel, buserr.WithDetail(constant.ErrPortInUsed, create.Port, nil)
}
}
}
if existDomain, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(create.Domain), websiteDomainRepo.WithPort(create.Port)); existDomain.ID > 0 {
website, _ := websiteRepo.GetFirst(commonRepo.WithByID(existDomain.WebsiteID))
return domainModel, buserr.WithName(constant.ErrDomainIsUsed, website.PrimaryDomain)
return nil, err
}
website, err := websiteRepo.GetFirst(commonRepo.WithByID(create.WebsiteID))
if err != nil {
return domainModel, err
}
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(create.WebsiteID), websiteDomainRepo.WithPort(create.Port)); len(oldDomains) == 0 {
ports = append(ports, create.Port)
return nil, err
}
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(create.WebsiteID), websiteDomainRepo.WithDomain(create.Domain)); len(oldDomains) == 0 {
domains = append(domains, create.Domain)
domainModels, addPorts, addDomains, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID)
if err != nil {
return nil, err
}
go func() {
_ = OperateFirewallPort(nil, addPorts)
}()
if err := addListenAndServerName(website, ports, domains); err != nil {
return domainModel, err
}
domainModel = model.WebsiteDomain{
Domain: create.Domain,
Port: create.Port,
WebsiteID: create.WebsiteID,
}
if create.Port != httpPort {
go func() {
_ = OperateFirewallPort(nil, []int{create.Port})
}()
if err := addListenAndServerName(website, addPorts, addDomains); err != nil {
return nil, err
}
return domainModel, websiteDomainRepo.Create(context.TODO(), &domainModel)
return domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels)
}
func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) {

@ -3,10 +3,13 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"gopkg.in/yaml.v3"
"path"
"reflect"
"strconv"
"strings"
"time"
@ -673,3 +676,70 @@ func changeServiceName(composePath, newServiceName string) (composeByte []byte,
return yaml.Marshal(composeMap)
}
func getWebsiteDomains(domains string, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
var (
ports map[int]struct{}
)
domainArray := strings.Split(domains, "\n")
for _, domain := range domainArray {
if domain == "" {
continue
}
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return
}
var domainModel model.WebsiteDomain
domainModel, err = getDomain(domain, defaultPort)
if err != nil {
return
}
if reflect.DeepEqual(domainModel, model.WebsiteDomain{}) {
continue
}
domainModel.WebsiteID = websiteID
domainModels = append(domainModels, domainModel)
if domainModel.Port != defaultPort {
ports[domainModel.Port] = struct{}{}
}
if exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domainModel.Domain), websiteDomainRepo.WithWebsiteId(websiteID)); exist.ID == 0 {
addDomains = append(addDomains, domainModel.Domain)
}
}
for _, domain := range domainModels {
if exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domain.Domain), websiteDomainRepo.WithPort(domain.Port)); exist.ID > 0 {
website, _ := websiteRepo.GetFirst(commonRepo.WithByID(exist.WebsiteID))
err = buserr.WithName(constant.ErrDomainIsUsed, website.PrimaryDomain)
return
}
}
for port := range ports {
addPorts = append(addPorts, port)
if existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) == 0 {
errMap := make(map[string]interface{})
errMap["port"] = port
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
if appInstall.ID > 0 {
errMap["type"] = i18n.GetMsgByKey("TYPE_APP")
errMap["name"] = appInstall.Name
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithPort(port))
if runtime != nil {
errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME")
errMap["name"] = runtime.Name
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
if common.ScanPort(port) {
err = buserr.WithDetail(constant.ErrPortInUsed, port, nil)
return
}
}
}
return
}

@ -76,6 +76,7 @@ ErrBackupExist: 'the backup file corresponds to a portion of the original data t
ErrPHPResource: 'The local runtime does not support switching'
ErrPathPermission: 'A folder with non-1000:1000 permissions was detected in the index directory, which may cause Access denied errors when accessing the website.'
ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
ErrDomainFormat: "{{ .name }} domain format error"
#ssl
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"

@ -76,6 +76,7 @@ ErrBackupExist: '該備份文件對應部分原數據不存在: {{ .detail}}"'
ErrPHPResource: '本地運行環境不支持切換!'
ErrPathPermission: 'index 目錄下檢測到非 1000:1000 權限文件夾,可能導致網站訪問 Access denied 錯誤'
ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正確"
#ssl
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"

@ -76,6 +76,7 @@ ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail}}"'
ErrPHPResource: '本地运行环境不支持切换!'
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误'
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正确"
#ssl
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"

@ -220,3 +220,9 @@ func ConvertToPinyin(text string) string {
return strings.Join(strArr, "")
}
func IsValidDomain(domain string) bool {
pattern := `^([\w\\u4e00-\\u9fa5\\-\\*]{1,100}\.){1,10}([\w\\u4e00-\\u9fa5\\-]{1,24}|[\w\\u4e00-\\u9fa5\\-]{1,24}\.[\w\\u4e00-\\u9fa5\\-]{1,24})$`
match, _ := regexp.MatchString(pattern, domain)
return match
}

@ -101,9 +101,8 @@ export namespace Website {
}
export interface DomainCreate {
websiteId: number;
port: number;
domain: string;
websiteID: number;
domains: string;
}
export interface DomainDelete {

@ -13,11 +13,13 @@
<el-row v-loading="loading">
<el-col :span="22" :offset="1">
<el-form ref="domainForm" label-position="top" :model="domain" :rules="rules">
<el-form-item :label="$t('website.domain')" prop="domain">
<el-input v-model="domain.domain"></el-input>
</el-form-item>
<el-form-item :label="$t('commons.table.port')" prop="port">
<el-input v-model.number="domain.port"></el-input>
<el-form-item :label="$t('website.domain')" prop="domains">
<el-input
type="textarea"
:autosize="{ minRows: 4, maxRows: 10 }"
v-model="domain.domains"
:placeholder="$t('website.domainHelper')"
></el-input>
</el-form-item>
</el-form>
</el-col>
@ -44,17 +46,15 @@ import { MsgSuccess } from '@/utils/message';
const domainForm = ref<FormInstance>();
let rules = ref({
domain: [Rules.requiredInput, Rules.domain],
port: [Rules.requiredInput],
const rules = ref({
domains: [Rules.requiredInput],
});
let open = ref(false);
let loading = ref(false);
let domain = ref({
websiteId: 0,
domain: '',
port: 80,
const open = ref(false);
const loading = ref(false);
const domain = ref({
websiteID: 0,
domains: '',
});
const em = defineEmits(['close']);
@ -65,7 +65,7 @@ const handleClose = () => {
};
const acceptParams = async (websiteId: number) => {
domain.value.websiteId = Number(websiteId);
domain.value.websiteID = Number(websiteId);
open.value = true;
};

Loading…
Cancel
Save