mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
feat: 域名支持批量添加 (#2774)
Refs https://github.com/1Panel-dev/1Panel/issues/2633
This commit is contained in:
parent
082032e15f
commit
eebef06c77
@ -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{}{}
|
||||
}
|
||||
domains, _, _, err = getWebsiteDomains(create.PrimaryDomain, defaultHttpPort, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return nil, 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)
|
||||
}
|
||||
|
||||
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, addPorts, addDomains); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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})
|
||||
}()
|
||||
}
|
||||
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…
x
Reference in New Issue
Block a user