mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-03 12:20:28 +08:00
优化域名服务相关功能
This commit is contained in:
1
go.sum
1
go.sum
@@ -9,6 +9,7 @@ github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiU
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
package domainutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 校验域名格式
|
||||
// ValidateDomainFormat 校验域名格式
|
||||
func ValidateDomainFormat(domain string) bool {
|
||||
pieces := strings.Split(domain, ".")
|
||||
for _, piece := range pieces {
|
||||
if !regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
|
||||
if piece == "-" ||
|
||||
strings.HasPrefix(piece, "-") ||
|
||||
strings.HasSuffix(piece, "-") ||
|
||||
strings.Contains(piece, "--") ||
|
||||
len(piece) > 63 ||
|
||||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一段不能是全数字
|
||||
if regexp.MustCompile(`^(\d+)$`).MatchString(pieces[len(pieces)-1]) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 转换线路列表
|
||||
// ConvertRoutesToMaps 转换线路列表
|
||||
func ConvertRoutesToMaps(routes []*pb.DNSRoute) []maps.Map {
|
||||
result := []maps.Map{}
|
||||
for _, route := range routes {
|
||||
@@ -32,7 +44,7 @@ func ConvertRoutesToMaps(routes []*pb.DNSRoute) []maps.Map {
|
||||
return result
|
||||
}
|
||||
|
||||
// 筛选线路
|
||||
// FilterRoutes 筛选线路
|
||||
func FilterRoutes(routes []*pb.DNSRoute, allRoutes []*pb.DNSRoute) []*pb.DNSRoute {
|
||||
routeCodes := []string{}
|
||||
for _, route := range allRoutes {
|
||||
@@ -46,3 +58,84 @@ func FilterRoutes(routes []*pb.DNSRoute, allRoutes []*pb.DNSRoute) []*pb.DNSRout
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ValidateRecordName 校验记录名
|
||||
func ValidateRecordName(name string) bool {
|
||||
if name == "*" || name == "@" || len(name) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
pieces := strings.Split(name, ".")
|
||||
for _, piece := range pieces {
|
||||
if piece == "-" ||
|
||||
strings.HasPrefix(piece, "-") ||
|
||||
strings.HasSuffix(piece, "-") ||
|
||||
strings.Contains(piece, "--") ||
|
||||
len(piece) > 63 ||
|
||||
!regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(piece) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidateRecordValue 校验记录值
|
||||
func ValidateRecordValue(recordType dnsconfigs.RecordType, value string) (message string, ok bool) {
|
||||
switch recordType {
|
||||
case dnsconfigs.RecordTypeA:
|
||||
if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(value) {
|
||||
message = "请输入正确格式的IP"
|
||||
return
|
||||
}
|
||||
if net.ParseIP(value) == nil {
|
||||
message = "请输入正确格式的IP"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeCNAME:
|
||||
if strings.HasSuffix(value, ".") {
|
||||
value = value[:len(value)-1]
|
||||
}
|
||||
if !strings.Contains(value, ".") || !ValidateDomainFormat(value) {
|
||||
message = "请输入正确的域名"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeAAAA:
|
||||
if !strings.Contains(value, ":") {
|
||||
message = "请输入正确格式的IPv6地址"
|
||||
return
|
||||
}
|
||||
if net.ParseIP(value) == nil {
|
||||
message = "请输入正确格式的IPv6地址"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeNS:
|
||||
if strings.HasSuffix(value, ".") {
|
||||
value = value[:len(value)-1]
|
||||
}
|
||||
if !strings.Contains(value, ".") || !ValidateDomainFormat(value) {
|
||||
message = "请输入正确的DNS服务器域名"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeMX:
|
||||
if strings.HasSuffix(value, ".") {
|
||||
value = value[:len(value)-1]
|
||||
}
|
||||
if !strings.Contains(value, ".") || !ValidateDomainFormat(value) {
|
||||
message = "请输入正确的邮件服务器域名"
|
||||
return
|
||||
}
|
||||
case dnsconfigs.RecordTypeTXT:
|
||||
if len(value) > 512 {
|
||||
message = "文本长度不能超出512字节"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(value) > 512 {
|
||||
message = "记录值长度不能超出512字节"
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package domainutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateRecordValue(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
|
||||
// A
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeA, "1.2")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeA, "1.2.3.400")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeA, "1.2.3.4")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
// CNAME
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeCNAME, "example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeCNAME, "example.com.")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeCNAME, "hello, world")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
// AAAA
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeAAAA, "1.2.3.4")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeAAAA, "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
// NS
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeNS, "1.2.3.4")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
{
|
||||
_, ok := ValidateRecordValue(dnsconfigs.RecordTypeNS, "example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CreateAction struct {
|
||||
@@ -34,6 +35,8 @@ func (this *CreateAction) RunPost(params struct {
|
||||
this.CreateLogInfo("创建域名 %d", domainId)
|
||||
}()
|
||||
|
||||
params.Name = strings.ToLower(params.Name)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入域名").
|
||||
|
||||
@@ -4,6 +4,7 @@ package records
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -63,6 +64,17 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
this.CreateLogInfo("创建域名记录 %d", recordId)
|
||||
}()
|
||||
|
||||
// 校验记录名
|
||||
if !domainutils.ValidateRecordName(params.Name) {
|
||||
this.FailField("name", "请输入正确的记录名")
|
||||
}
|
||||
|
||||
// 校验记录值
|
||||
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
|
||||
if !ok {
|
||||
this.FailField("value", "记录值错误:"+message)
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().NSRecordRPC().CreateNSRecord(this.AdminContext(), &pb.CreateNSRecordRequest{
|
||||
NsDomainId: params.DomainId,
|
||||
Description: params.Description,
|
||||
|
||||
@@ -4,6 +4,7 @@ package records
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
@@ -87,6 +88,17 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
}) {
|
||||
this.CreateLogInfo("修改域名记录 %d", params.RecordId)
|
||||
|
||||
// 校验记录名
|
||||
if !domainutils.ValidateRecordName(params.Name) {
|
||||
this.FailField("name", "请输入正确的记录名")
|
||||
}
|
||||
|
||||
// 校验记录值
|
||||
message, ok := domainutils.ValidateRecordValue(params.Type, params.Value)
|
||||
if !ok {
|
||||
this.FailField("value", "记录值错误:"+message)
|
||||
}
|
||||
|
||||
_, err := this.RPC().NSRecordRPC().UpdateNSRecord(this.AdminContext(), &pb.UpdateNSRecordRequest{
|
||||
NsRecordId: params.RecordId,
|
||||
Description: params.Description,
|
||||
|
||||
@@ -4,7 +4,6 @@ package domains
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/dns/domains/domainutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -57,7 +56,6 @@ func (this *UpdateAction) RunGet(params struct {
|
||||
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
DomainId int64
|
||||
Name string
|
||||
ClusterId int64
|
||||
UserId int64
|
||||
IsOn bool
|
||||
@@ -68,15 +66,6 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
this.CreateLogInfo("修改域名 %d", params.DomainId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
Require("请输入域名").
|
||||
Expect(func() (message string, success bool) {
|
||||
success = domainutils.ValidateDomainFormat(params.Name)
|
||||
if !success {
|
||||
message = "请输入正确的域名"
|
||||
}
|
||||
return
|
||||
}).
|
||||
Field("clusterId", params.ClusterId).
|
||||
Gt(0, "请选择所属集群")
|
||||
|
||||
@@ -84,7 +73,6 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
NsDomainId: params.DomainId,
|
||||
NsClusterId: params.ClusterId,
|
||||
UserId: params.UserId,
|
||||
Name: params.Name,
|
||||
IsOn: params.IsOn,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -51,8 +51,10 @@ Vue.component("ns-route-ranges-box", {
|
||||
|
||||
this.ranges.push({
|
||||
type: "ipRange",
|
||||
params: {
|
||||
ipFrom: this.ipRangeFrom,
|
||||
ipTo: this.ipRangeTo
|
||||
}
|
||||
})
|
||||
this.cancelIPRange()
|
||||
},
|
||||
@@ -76,7 +78,7 @@ Vue.component("ns-route-ranges-box", {
|
||||
<div v-if="ranges.length > 0">
|
||||
<div class="ui label tiny basic" v-for="(range, index) in ranges">
|
||||
<span v-if="range.type == 'ipRange'">IP范围:</span>
|
||||
{{range.ipFrom}} - {{range.ipTo}} <a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
{{range.params.ipFrom}} - {{range.params.ipTo}} <a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{$layout}
|
||||
{$template "../menu"}
|
||||
{$template "menu"}
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||
@@ -17,11 +17,6 @@
|
||||
<p class="comment">用于访问节点和域名解析等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<more-options-indicator></more-options-indicator>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -39,12 +39,12 @@
|
||||
<td>
|
||||
<div v-if="node.status.isActive">
|
||||
<span class="green">运行中</span>
|
||||
<a href="" @click.prevent="stopNode()" v-if="!isStopping"><span>[通过SSH停止]</span></a>
|
||||
<!--<a href="" @click.prevent="stopNode()" v-if="!isStopping"><span>[通过SSH停止]</span></a>-->
|
||||
<span v-if="isStopping">[停止中...]</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="red">已断开</span>
|
||||
<a href="" @click.prevent="startNode()" v-if="node.isInstalled && !isStarting"><span>[通过SSH启动]</span></a>
|
||||
<!--<a href="" @click.prevent="startNode()" v-if="node.isInstalled && !isStarting"><span>[通过SSH启动]</span></a>-->
|
||||
<span v-if="node.isInstalled && isStarting">[启动中...]</span>
|
||||
<a v-if="!node.isInstalled" :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" ><span>去安装></span></a>
|
||||
</div>
|
||||
@@ -59,20 +59,10 @@
|
||||
<td>内存用量</td>
|
||||
<td>{{node.status.memUsageText}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>连接数</td>
|
||||
<td>{{node.status.connectionCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>负载</td>
|
||||
<td>{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}} <tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缓存用量</td>
|
||||
<td>
|
||||
磁盘:{{node.status.cacheTotalDiskSize}} 内存:{{node.status.cacheTotalMemorySize}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>版本</td>
|
||||
<td>v{{node.status.buildVersion}}
|
||||
@@ -105,7 +95,7 @@
|
||||
<td>是否已安装</td>
|
||||
<td>
|
||||
<span v-if="node.isInstalled" class="green">已安装</span>
|
||||
<a v-else :href="'/ns/clusters/cluster/installNode?clusterId=' + clusterId + '&nodeId=' + nodeId" class="underline" title="点击进入安装界面"><span class="red">未安装</span></a>
|
||||
<a v-else :href="'/ns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" class="underline" title="点击进入安装界面"><span class="red">未安装</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -14,12 +14,14 @@
|
||||
<td class="title">所属集群 *</td>
|
||||
<td>
|
||||
<ns-cluster-selector></ns-cluster-selector>
|
||||
<p class="comment">需要部署域名服务的集群。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属用户</td>
|
||||
<td>
|
||||
<ns-user-selector></ns-user-selector>
|
||||
<p class="comment">当前域名所属的平台用户。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<tr>
|
||||
<td>记录值</td>
|
||||
<td>
|
||||
<input type="text" name="value" maxlength="1024"/>
|
||||
<input type="text" name="value" maxlength="512"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Tea.context(function () {
|
||||
this.createRecord = function () {
|
||||
teaweb.popup("/ns/domains/records/createPopup?domainId=" + this.domain.id, {
|
||||
height: "32em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
@@ -11,6 +12,7 @@ Tea.context(function () {
|
||||
|
||||
this.updateRecord = function (recordId) {
|
||||
teaweb.popup("/ns/domains/records/updatePopup?recordId=" + recordId, {
|
||||
height: "32em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<tr>
|
||||
<td>记录值</td>
|
||||
<td>
|
||||
<input type="text" name="value" maxlength="1024" v-model="record.value"/>
|
||||
<input type="text" name="value" maxlength="512" v-model="record.value"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -8,19 +8,21 @@
|
||||
<tr>
|
||||
<td>域名 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="255" ref="focus" v-model="domain.name"/>
|
||||
{{domain.name}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">所属集群 *</td>
|
||||
<td>
|
||||
<ns-cluster-selector :v-cluster-id="domain.clusterId"></ns-cluster-selector>
|
||||
<p class="comment">需要部署域名服务的集群。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属用户</td>
|
||||
<td>
|
||||
<ns-user-selector :v-user-id="domain.userId"></ns-user-selector>
|
||||
<p class="comment">当前域名所属的平台用户。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user