mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-17 03:04:46 +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 {
|
type WebsiteDomainCreate struct {
|
||||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||||
Port int `json:"port" validate:"required"`
|
Domains string `json:"domains" validate:"required"`
|
||||||
Domain string `json:"domain" validate:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteDomainDelete struct {
|
type WebsiteDomainDelete struct {
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||||
@ -59,7 +58,7 @@ type IWebsiteService interface {
|
|||||||
UpdateWebsite(req request.WebsiteUpdate) error
|
UpdateWebsite(req request.WebsiteUpdate) error
|
||||||
DeleteWebsite(req request.WebsiteDelete) error
|
DeleteWebsite(req request.WebsiteDelete) error
|
||||||
GetWebsite(id uint) (response.WebsiteDTO, 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)
|
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
|
||||||
DeleteWebsiteDomain(domainId uint) error
|
DeleteWebsiteDomain(domainId uint) error
|
||||||
|
|
||||||
@ -188,56 +187,18 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
|||||||
defaultHttpPort := nginxInstall.HttpPort
|
defaultHttpPort := nginxInstall.HttpPort
|
||||||
|
|
||||||
var (
|
var (
|
||||||
domains []model.WebsiteDomain
|
otherDomains []model.WebsiteDomain
|
||||||
ports = make(map[int]struct{})
|
domains []model.WebsiteDomain
|
||||||
)
|
)
|
||||||
domainArray := strings.Split(create.OtherDomains, "\n")
|
domains, _, _, err = getWebsiteDomains(create.PrimaryDomain, defaultHttpPort, 0)
|
||||||
domainArray = append(domainArray, create.PrimaryDomain)
|
if err != nil {
|
||||||
for _, domain := range domainArray {
|
return err
|
||||||
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{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
otherDomains, _, _, err = getWebsiteDomains(create.OtherDomains, defaultHttpPort, 0)
|
||||||
for _, domain := range domains {
|
if err != nil {
|
||||||
if exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domain.Domain), websiteDomainRepo.WithPort(domain.Port)); exist.ID > 0 {
|
return err
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
domains = append(domains, otherDomains...)
|
||||||
|
|
||||||
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
||||||
website := &model.Website{
|
website := &model.Website{
|
||||||
@ -487,59 +448,34 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) (model.WebsiteDomain, error) {
|
func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) {
|
||||||
var (
|
var (
|
||||||
domainModel model.WebsiteDomain
|
domainModels []model.WebsiteDomain
|
||||||
ports []int
|
addPorts []int
|
||||||
domains []string
|
addDomains []string
|
||||||
)
|
)
|
||||||
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
|
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
|
||||||
if err != nil {
|
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))
|
website, err := websiteRepo.GetFirst(commonRepo.WithByID(create.WebsiteID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domainModel, err
|
return nil, err
|
||||||
}
|
|
||||||
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(create.WebsiteID), websiteDomainRepo.WithPort(create.Port)); len(oldDomains) == 0 {
|
|
||||||
ports = append(ports, create.Port)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(create.WebsiteID), websiteDomainRepo.WithDomain(create.Domain)); len(oldDomains) == 0 {
|
domainModels, addPorts, addDomains, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID)
|
||||||
domains = append(domains, create.Domain)
|
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 domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) {
|
func (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) {
|
||||||
|
@ -3,10 +3,13 @@ package service
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"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/cmd"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -673,3 +676,70 @@ func changeServiceName(composePath, newServiceName string) (composeByte []byte,
|
|||||||
|
|
||||||
return yaml.Marshal(composeMap)
|
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!'
|
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.'
|
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 }}"
|
ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
|
||||||
|
ErrDomainFormat: "{{ .name }} domain format error"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||||
|
@ -76,6 +76,7 @@ ErrBackupExist: '該備份文件對應部分原數據不存在: {{ .detail}}"'
|
|||||||
ErrPHPResource: '本地運行環境不支持切換!'
|
ErrPHPResource: '本地運行環境不支持切換!'
|
||||||
ErrPathPermission: 'index 目錄下檢測到非 1000:1000 權限文件夾,可能導致網站訪問 Access denied 錯誤'
|
ErrPathPermission: 'index 目錄下檢測到非 1000:1000 權限文件夾,可能導致網站訪問 Access denied 錯誤'
|
||||||
ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
|
ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
|
||||||
|
ErrDomainFormat: "{{ .name }} 域名格式不正確"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
|
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
|
||||||
|
@ -76,6 +76,7 @@ ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail}}"'
|
|||||||
ErrPHPResource: '本地运行环境不支持切换!'
|
ErrPHPResource: '本地运行环境不支持切换!'
|
||||||
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误'
|
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误'
|
||||||
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
|
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
|
||||||
|
ErrDomainFormat: "{{ .name }} 域名格式不正确"
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||||
|
@ -220,3 +220,9 @@ func ConvertToPinyin(text string) string {
|
|||||||
|
|
||||||
return strings.Join(strArr, "")
|
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 {
|
export interface DomainCreate {
|
||||||
websiteId: number;
|
websiteID: number;
|
||||||
port: number;
|
domains: string;
|
||||||
domain: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DomainDelete {
|
export interface DomainDelete {
|
||||||
|
@ -13,11 +13,13 @@
|
|||||||
<el-row v-loading="loading">
|
<el-row v-loading="loading">
|
||||||
<el-col :span="22" :offset="1">
|
<el-col :span="22" :offset="1">
|
||||||
<el-form ref="domainForm" label-position="top" :model="domain" :rules="rules">
|
<el-form ref="domainForm" label-position="top" :model="domain" :rules="rules">
|
||||||
<el-form-item :label="$t('website.domain')" prop="domain">
|
<el-form-item :label="$t('website.domain')" prop="domains">
|
||||||
<el-input v-model="domain.domain"></el-input>
|
<el-input
|
||||||
</el-form-item>
|
type="textarea"
|
||||||
<el-form-item :label="$t('commons.table.port')" prop="port">
|
:autosize="{ minRows: 4, maxRows: 10 }"
|
||||||
<el-input v-model.number="domain.port"></el-input>
|
v-model="domain.domains"
|
||||||
|
:placeholder="$t('website.domainHelper')"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -44,17 +46,15 @@ import { MsgSuccess } from '@/utils/message';
|
|||||||
|
|
||||||
const domainForm = ref<FormInstance>();
|
const domainForm = ref<FormInstance>();
|
||||||
|
|
||||||
let rules = ref({
|
const rules = ref({
|
||||||
domain: [Rules.requiredInput, Rules.domain],
|
domains: [Rules.requiredInput],
|
||||||
port: [Rules.requiredInput],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let open = ref(false);
|
const open = ref(false);
|
||||||
let loading = ref(false);
|
const loading = ref(false);
|
||||||
let domain = ref({
|
const domain = ref({
|
||||||
websiteId: 0,
|
websiteID: 0,
|
||||||
domain: '',
|
domains: '',
|
||||||
port: 80,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const em = defineEmits(['close']);
|
const em = defineEmits(['close']);
|
||||||
@ -65,7 +65,7 @@ const handleClose = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const acceptParams = async (websiteId: number) => {
|
const acceptParams = async (websiteId: number) => {
|
||||||
domain.value.websiteId = Number(websiteId);
|
domain.value.websiteID = Number(websiteId);
|
||||||
open.value = true;
|
open.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user