mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 05:00:25 +08:00 
			
		
		
		
	IP名单新增IPv6和所有IP两种类型
This commit is contained in:
		@@ -2,18 +2,42 @@ package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"net"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 将IP转换为整型
 | 
			
		||||
func IP2Long(ip string) uint32 {
 | 
			
		||||
func IP2Long(ip string) uint64 {
 | 
			
		||||
	s := net.ParseIP(ip)
 | 
			
		||||
	if s == nil {
 | 
			
		||||
	if len(s) != 16 {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(s) == 16 {
 | 
			
		||||
		return binary.BigEndian.Uint32(s[12:16])
 | 
			
		||||
	if strings.Contains(ip, ":") { // IPv6
 | 
			
		||||
		bigInt := big.NewInt(0)
 | 
			
		||||
		bigInt.SetBytes(s.To16())
 | 
			
		||||
		return bigInt.Uint64()
 | 
			
		||||
	}
 | 
			
		||||
	return binary.BigEndian.Uint32(s)
 | 
			
		||||
	return uint64(binary.BigEndian.Uint32(s.To4()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 判断是否为IPv4
 | 
			
		||||
func IsIPv4(ip string) bool {
 | 
			
		||||
	if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if IP2Long(ip) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 判断是否为IPv6
 | 
			
		||||
func IsIPv6(ip string) bool {
 | 
			
		||||
	if !strings.Contains(ip, ":") {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return len(net.ParseIP(ip)) == net.IPv6len
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								internal/utils/ip_utils_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								internal/utils/ip_utils_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestIP2Long(t *testing.T) {
 | 
			
		||||
	for _, ip := range []string{
 | 
			
		||||
		"0.0.0.1",
 | 
			
		||||
		"0.0.0.2",
 | 
			
		||||
		"127.0.0.1",
 | 
			
		||||
		"192.0.0.2",
 | 
			
		||||
		"255.255.255.255",
 | 
			
		||||
		"2001:db8:0:1::101",
 | 
			
		||||
		"2001:db8:0:1::102",
 | 
			
		||||
		"2406:8c00:0:3409:133:18:203:0",
 | 
			
		||||
		"2406:8c00:0:3409:133:18:203:158",
 | 
			
		||||
		"2406:8c00:0:3409:133:18:203:159",
 | 
			
		||||
		"2406:8c00:0:3409:133:18:203:160",
 | 
			
		||||
	} {
 | 
			
		||||
		t.Log(ip, " -> ", IP2Long(ip))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIsIPv4(t *testing.T) {
 | 
			
		||||
	type testIP struct {
 | 
			
		||||
		ip string
 | 
			
		||||
		ok bool
 | 
			
		||||
	}
 | 
			
		||||
	for _, item := range []testIP{
 | 
			
		||||
		{
 | 
			
		||||
			ip: "1",
 | 
			
		||||
			ok: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "192.168.0.1",
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "1.1.0.1",
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "255.255.255.255",
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "192.168.0.1233",
 | 
			
		||||
			ok: false,
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		if IsIPv4(item.ip) != item.ok {
 | 
			
		||||
			t.Fatal(item.ip, "should be", item.ok)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIsIPv6(t *testing.T) {
 | 
			
		||||
	type testIP struct {
 | 
			
		||||
		ip string
 | 
			
		||||
		ok bool
 | 
			
		||||
	}
 | 
			
		||||
	for _, item := range []testIP{
 | 
			
		||||
		{
 | 
			
		||||
			ip: "1",
 | 
			
		||||
			ok: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "2406:8c00:0:3409:133:18:203:158",
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "2406::8c00:0:3409:133:18:203:158",
 | 
			
		||||
			ok: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ip: "2001:db8:0:1::101",
 | 
			
		||||
			ok: true,
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		if IsIPv6(item.ip) != item.ok {
 | 
			
		||||
			t.Fatal(item.ip, "should be", item.ok)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -21,7 +21,7 @@ func (this *CreateIPPopupAction) RunGet(params struct {
 | 
			
		||||
	FirewallPolicyId int64
 | 
			
		||||
	Type             string
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["type"] = params.Type
 | 
			
		||||
	this.Data["listType"] = params.Type
 | 
			
		||||
 | 
			
		||||
	listId, err := dao.SharedHTTPFirewallPolicyDAO.FindEnabledPolicyIPListIdWithType(this.AdminContext(), params.FirewallPolicyId, params.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -40,33 +40,46 @@ func (this *CreateIPPopupAction) RunPost(params struct {
 | 
			
		||||
	IpTo             string
 | 
			
		||||
	ExpiredAt        int64
 | 
			
		||||
	Reason           string
 | 
			
		||||
	Type             string
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	// TODO 校验ListId所属用户
 | 
			
		||||
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("ipFrom", params.IpFrom).
 | 
			
		||||
		Require("请输入开始IP")
 | 
			
		||||
	switch params.Type {
 | 
			
		||||
	case "ipv4":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入开始IP")
 | 
			
		||||
 | 
			
		||||
	// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
	ipFromLong := utils.IP2Long(params.IpFrom)
 | 
			
		||||
	if len(params.IpFrom) > 0 {
 | 
			
		||||
		if ipFromLong == 0 {
 | 
			
		||||
		// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
		var ipFromLong uint64
 | 
			
		||||
		if !utils.IsIPv4(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的开始IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		ipFromLong = utils.IP2Long(params.IpFrom)
 | 
			
		||||
 | 
			
		||||
	ipToLong := utils.IP2Long(params.IpTo)
 | 
			
		||||
	if len(params.IpTo) > 0 {
 | 
			
		||||
		if ipToLong == 0 {
 | 
			
		||||
		var ipToLong uint64
 | 
			
		||||
		if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
 | 
			
		||||
			ipToLong = utils.IP2Long(params.IpTo)
 | 
			
		||||
			this.Fail("请输入正确的结束IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
		params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
			params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		}
 | 
			
		||||
	case "ipv6":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入IP")
 | 
			
		||||
 | 
			
		||||
		// 校验IP格式(ipFrom)
 | 
			
		||||
		if !utils.IsIPv6(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的IPv6地址")
 | 
			
		||||
		}
 | 
			
		||||
	case "all":
 | 
			
		||||
		params.IpFrom = "0.0.0.0"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
 | 
			
		||||
@@ -75,6 +88,7 @@ func (this *CreateIPPopupAction) RunPost(params struct {
 | 
			
		||||
		IpTo:      params.IpTo,
 | 
			
		||||
		ExpiredAt: params.ExpiredAt,
 | 
			
		||||
		Reason:    params.Reason,
 | 
			
		||||
		Type:      params.Type,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -63,6 +63,7 @@ func (this *ListsAction) RunGet(params struct {
 | 
			
		||||
			"ipTo":        item.IpTo,
 | 
			
		||||
			"expiredTime": expiredTime,
 | 
			
		||||
			"reason":      item.Reason,
 | 
			
		||||
			"type":        item.Type,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["items"] = itemMaps
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,11 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
 | 
			
		||||
		"ipTo":      item.IpTo,
 | 
			
		||||
		"expiredAt": item.ExpiredAt,
 | 
			
		||||
		"reason":    item.Reason,
 | 
			
		||||
		"type":      item.Type,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["type"] = item.Type
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +53,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
	IpTo      string
 | 
			
		||||
	ExpiredAt int64
 | 
			
		||||
	Reason    string
 | 
			
		||||
	Type      string
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
@@ -59,27 +63,39 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
 | 
			
		||||
	// TODO 校验ItemId所属用户
 | 
			
		||||
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("ipFrom", params.IpFrom).
 | 
			
		||||
		Require("请输入开始IP")
 | 
			
		||||
	switch params.Type {
 | 
			
		||||
	case "ipv4":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入开始IP")
 | 
			
		||||
 | 
			
		||||
	// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
	ipFromLong := utils.IP2Long(params.IpFrom)
 | 
			
		||||
	if len(params.IpFrom) > 0 {
 | 
			
		||||
		if ipFromLong == 0 {
 | 
			
		||||
		// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
		var ipFromLong uint64
 | 
			
		||||
		if !utils.IsIPv4(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的开始IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		ipFromLong = utils.IP2Long(params.IpFrom)
 | 
			
		||||
 | 
			
		||||
	ipToLong := utils.IP2Long(params.IpTo)
 | 
			
		||||
	if len(params.IpTo) > 0 {
 | 
			
		||||
		if ipToLong == 0 {
 | 
			
		||||
		var ipToLong uint64
 | 
			
		||||
		if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
 | 
			
		||||
			ipToLong = utils.IP2Long(params.IpTo)
 | 
			
		||||
			this.Fail("请输入正确的结束IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
		params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
			params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		}
 | 
			
		||||
	case "ipv6":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入IP")
 | 
			
		||||
 | 
			
		||||
		// 校验IP格式(ipFrom)
 | 
			
		||||
		if !utils.IsIPv6(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的IPv6地址")
 | 
			
		||||
		}
 | 
			
		||||
	case "all":
 | 
			
		||||
		params.IpFrom = "0.0.0.0"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
 | 
			
		||||
@@ -88,6 +104,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
		IpTo:      params.IpTo,
 | 
			
		||||
		ExpiredAt: params.ExpiredAt,
 | 
			
		||||
		Reason:    params.Reason,
 | 
			
		||||
		Type:      params.Type,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ func (this *GroupsAction) Init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *GroupsAction) RunGet(params struct {
 | 
			
		||||
	ServerId         int64
 | 
			
		||||
	FirewallPolicyId int64
 | 
			
		||||
	Type             string
 | 
			
		||||
}) {
 | 
			
		||||
@@ -70,5 +71,13 @@ func (this *GroupsAction) RunGet(params struct {
 | 
			
		||||
 | 
			
		||||
	this.Data["groups"] = groupMaps
 | 
			
		||||
 | 
			
		||||
	// WAF是否启用
 | 
			
		||||
	webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AllowListAction struct {
 | 
			
		||||
@@ -74,9 +75,19 @@ func (this *AllowListAction) RunGet(params struct {
 | 
			
		||||
			"ipTo":        item.IpTo,
 | 
			
		||||
			"expiredTime": expiredTime,
 | 
			
		||||
			"reason":      item.Reason,
 | 
			
		||||
			"type":        item.Type,
 | 
			
		||||
			"isExpired":   item.ExpiredAt > 0 && item.ExpiredAt < time.Now().Unix(),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["items"] = itemMaps
 | 
			
		||||
 | 
			
		||||
	// WAF是否启用
 | 
			
		||||
	webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ func (this *CreateIPPopupAction) RunGet(params struct {
 | 
			
		||||
	ListId int64
 | 
			
		||||
	Type   string
 | 
			
		||||
}) {
 | 
			
		||||
	this.Data["type"] = params.Type
 | 
			
		||||
	this.Data["listType"] = params.Type
 | 
			
		||||
	this.Data["listId"] = params.ListId
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
@@ -32,33 +32,46 @@ func (this *CreateIPPopupAction) RunPost(params struct {
 | 
			
		||||
	IpTo      string
 | 
			
		||||
	ExpiredAt int64
 | 
			
		||||
	Reason    string
 | 
			
		||||
	Type      string
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
}) {
 | 
			
		||||
	// TODO 校验ListId所属用户
 | 
			
		||||
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("ipFrom", params.IpFrom).
 | 
			
		||||
		Require("请输入开始IP")
 | 
			
		||||
	switch params.Type {
 | 
			
		||||
	case "ipv4":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入开始IP")
 | 
			
		||||
 | 
			
		||||
	// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
	ipFromLong := utils.IP2Long(params.IpFrom)
 | 
			
		||||
	if len(params.IpFrom) > 0 {
 | 
			
		||||
		if ipFromLong == 0 {
 | 
			
		||||
		// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
		var ipFromLong uint64
 | 
			
		||||
		if !utils.IsIPv4(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的开始IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		ipFromLong = utils.IP2Long(params.IpFrom)
 | 
			
		||||
 | 
			
		||||
	ipToLong := utils.IP2Long(params.IpTo)
 | 
			
		||||
	if len(params.IpTo) > 0 {
 | 
			
		||||
		if ipToLong == 0 {
 | 
			
		||||
		var ipToLong uint64
 | 
			
		||||
		if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
 | 
			
		||||
			ipToLong = utils.IP2Long(params.IpTo)
 | 
			
		||||
			this.Fail("请输入正确的结束IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
		params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
			params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		}
 | 
			
		||||
	case "ipv6":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入IP")
 | 
			
		||||
 | 
			
		||||
		// 校验IP格式(ipFrom)
 | 
			
		||||
		if !utils.IsIPv6(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的IPv6地址")
 | 
			
		||||
		}
 | 
			
		||||
	case "all":
 | 
			
		||||
		params.IpFrom = "0.0.0.0"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createResp, err := this.RPC().IPItemRPC().CreateIPItem(this.AdminContext(), &pb.CreateIPItemRequest{
 | 
			
		||||
@@ -67,6 +80,7 @@ func (this *CreateIPPopupAction) RunPost(params struct {
 | 
			
		||||
		IpTo:      params.IpTo,
 | 
			
		||||
		ExpiredAt: params.ExpiredAt,
 | 
			
		||||
		Reason:    params.Reason,
 | 
			
		||||
		Type:      params.Type,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DenyListAction struct {
 | 
			
		||||
@@ -74,9 +75,19 @@ func (this *DenyListAction) RunGet(params struct {
 | 
			
		||||
			"ipTo":        item.IpTo,
 | 
			
		||||
			"expiredTime": expiredTime,
 | 
			
		||||
			"reason":      item.Reason,
 | 
			
		||||
			"type":        item.Type,
 | 
			
		||||
			"isExpired":   item.ExpiredAt > 0 && item.ExpiredAt < time.Now().Unix(),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["items"] = itemMaps
 | 
			
		||||
 | 
			
		||||
	// WAF是否启用
 | 
			
		||||
	webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["wafIsOn"] = webConfig.FirewallRef != nil && webConfig.FirewallRef.IsOn
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,11 @@ func (this *UpdateIPPopupAction) RunGet(params struct {
 | 
			
		||||
		"ipTo":      item.IpTo,
 | 
			
		||||
		"expiredAt": item.ExpiredAt,
 | 
			
		||||
		"reason":    item.Reason,
 | 
			
		||||
		"type":      item.Type,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Data["type"] = item.Type
 | 
			
		||||
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -49,6 +52,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
	IpTo      string
 | 
			
		||||
	ExpiredAt int64
 | 
			
		||||
	Reason    string
 | 
			
		||||
	Type      string
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
	CSRF *actionutils.CSRF
 | 
			
		||||
@@ -58,27 +62,39 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
 | 
			
		||||
	// TODO 校验ItemId所属用户
 | 
			
		||||
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("ipFrom", params.IpFrom).
 | 
			
		||||
		Require("请输入开始IP")
 | 
			
		||||
	switch params.Type {
 | 
			
		||||
	case "ipv4":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入开始IP")
 | 
			
		||||
 | 
			
		||||
	// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
	ipFromLong := utils.IP2Long(params.IpFrom)
 | 
			
		||||
	if len(params.IpFrom) > 0 {
 | 
			
		||||
		if ipFromLong == 0 {
 | 
			
		||||
		// 校验IP格式(ipFrom/ipTo)
 | 
			
		||||
		var ipFromLong uint64
 | 
			
		||||
		if !utils.IsIPv4(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的开始IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		ipFromLong = utils.IP2Long(params.IpFrom)
 | 
			
		||||
 | 
			
		||||
	ipToLong := utils.IP2Long(params.IpTo)
 | 
			
		||||
	if len(params.IpTo) > 0 {
 | 
			
		||||
		if ipToLong == 0 {
 | 
			
		||||
		var ipToLong uint64
 | 
			
		||||
		if len(params.IpTo) > 0 && !utils.IsIPv4(params.IpTo) {
 | 
			
		||||
			ipToLong = utils.IP2Long(params.IpTo)
 | 
			
		||||
			this.Fail("请输入正确的结束IP")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
		params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		if ipFromLong > 0 && ipToLong > 0 && ipFromLong > ipToLong {
 | 
			
		||||
			params.IpTo, params.IpFrom = params.IpFrom, params.IpTo
 | 
			
		||||
		}
 | 
			
		||||
	case "ipv6":
 | 
			
		||||
		params.Must.
 | 
			
		||||
			Field("ipFrom", params.IpFrom).
 | 
			
		||||
			Require("请输入IP")
 | 
			
		||||
 | 
			
		||||
		// 校验IP格式(ipFrom)
 | 
			
		||||
		if !utils.IsIPv6(params.IpFrom) {
 | 
			
		||||
			this.Fail("请输入正确的IPv6地址")
 | 
			
		||||
		}
 | 
			
		||||
	case "all":
 | 
			
		||||
		params.IpFrom = "0.0.0.0"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := this.RPC().IPItemRPC().UpdateIPItem(this.AdminContext(), &pb.UpdateIPItemRequest{
 | 
			
		||||
@@ -87,6 +103,7 @@ func (this *UpdateIPPopupAction) RunPost(params struct {
 | 
			
		||||
		IpTo:      params.IpTo,
 | 
			
		||||
		ExpiredAt: params.ExpiredAt,
 | 
			
		||||
		Reason:    params.Reason,
 | 
			
		||||
		Type:      params.Type,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,144 +1,154 @@
 | 
			
		||||
Vue.component("datetime-input", {
 | 
			
		||||
	props: ["v-name", "v-timestamp"],
 | 
			
		||||
	data: function () {
 | 
			
		||||
		let timestamp = this.vTimestamp
 | 
			
		||||
		if (timestamp != null) {
 | 
			
		||||
			timestamp = parseInt(timestamp)
 | 
			
		||||
			if (isNaN(timestamp)) {
 | 
			
		||||
				timestamp = 0
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			timestamp = 0
 | 
			
		||||
		}
 | 
			
		||||
    props: ["v-name", "v-timestamp"],
 | 
			
		||||
    mounted: function () {
 | 
			
		||||
        let that = this
 | 
			
		||||
        teaweb.datepicker(this.$refs.dayInput, function (v) {
 | 
			
		||||
            that.day = v
 | 
			
		||||
            that.hour = "23"
 | 
			
		||||
            that.minute = "59"
 | 
			
		||||
            that.second = "59"
 | 
			
		||||
            that.change()
 | 
			
		||||
        })
 | 
			
		||||
    },
 | 
			
		||||
    data: function () {
 | 
			
		||||
        let timestamp = this.vTimestamp
 | 
			
		||||
        if (timestamp != null) {
 | 
			
		||||
            timestamp = parseInt(timestamp)
 | 
			
		||||
            if (isNaN(timestamp)) {
 | 
			
		||||
                timestamp = 0
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            timestamp = 0
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		let day = ""
 | 
			
		||||
		let hour = ""
 | 
			
		||||
		let minute = ""
 | 
			
		||||
		let second = ""
 | 
			
		||||
        let day = ""
 | 
			
		||||
        let hour = ""
 | 
			
		||||
        let minute = ""
 | 
			
		||||
        let second = ""
 | 
			
		||||
 | 
			
		||||
		if (timestamp > 0) {
 | 
			
		||||
			let date = new Date()
 | 
			
		||||
			date.setTime(timestamp * 1000)
 | 
			
		||||
        if (timestamp > 0) {
 | 
			
		||||
            let date = new Date()
 | 
			
		||||
            date.setTime(timestamp * 1000)
 | 
			
		||||
 | 
			
		||||
			let year = date.getFullYear().toString()
 | 
			
		||||
			let month = this.leadingZero((date.getMonth() + 1).toString(), 2)
 | 
			
		||||
			day = year + "-" + month + "-" + this.leadingZero(date.getDate().toString(), 2)
 | 
			
		||||
            let year = date.getFullYear().toString()
 | 
			
		||||
            let month = this.leadingZero((date.getMonth() + 1).toString(), 2)
 | 
			
		||||
            day = year + "-" + month + "-" + this.leadingZero(date.getDate().toString(), 2)
 | 
			
		||||
 | 
			
		||||
			hour = this.leadingZero(date.getHours().toString(), 2)
 | 
			
		||||
			minute = this.leadingZero(date.getMinutes().toString(), 2)
 | 
			
		||||
			second = this.leadingZero(date.getSeconds().toString(), 2)
 | 
			
		||||
		}
 | 
			
		||||
            hour = this.leadingZero(date.getHours().toString(), 2)
 | 
			
		||||
            minute = this.leadingZero(date.getMinutes().toString(), 2)
 | 
			
		||||
            second = this.leadingZero(date.getSeconds().toString(), 2)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			timestamp: timestamp,
 | 
			
		||||
			day: day,
 | 
			
		||||
			hour: hour,
 | 
			
		||||
			minute: minute,
 | 
			
		||||
			second: second,
 | 
			
		||||
        return {
 | 
			
		||||
            timestamp: timestamp,
 | 
			
		||||
            day: day,
 | 
			
		||||
            hour: hour,
 | 
			
		||||
            minute: minute,
 | 
			
		||||
            second: second,
 | 
			
		||||
 | 
			
		||||
			hasDayError: false,
 | 
			
		||||
			hasHourError: false,
 | 
			
		||||
			hasMinuteError: false,
 | 
			
		||||
			hasSecondError: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		change: function () {
 | 
			
		||||
			let date = new Date()
 | 
			
		||||
            hasDayError: false,
 | 
			
		||||
            hasHourError: false,
 | 
			
		||||
            hasMinuteError: false,
 | 
			
		||||
            hasSecondError: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        change: function () {
 | 
			
		||||
            let date = new Date()
 | 
			
		||||
 | 
			
		||||
			// day
 | 
			
		||||
			if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
 | 
			
		||||
				this.hasDayError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			let pieces = this.day.split("-")
 | 
			
		||||
			let year = parseInt(pieces[0])
 | 
			
		||||
			date.setFullYear(year)
 | 
			
		||||
            // day
 | 
			
		||||
            if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(this.day)) {
 | 
			
		||||
                this.hasDayError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            let pieces = this.day.split("-")
 | 
			
		||||
            let year = parseInt(pieces[0])
 | 
			
		||||
            date.setFullYear(year)
 | 
			
		||||
 | 
			
		||||
			let month = parseInt(pieces[1])
 | 
			
		||||
			if (month < 1 || month > 12) {
 | 
			
		||||
				this.hasDayError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			date.setMonth(month - 1)
 | 
			
		||||
            let month = parseInt(pieces[1])
 | 
			
		||||
            if (month < 1 || month > 12) {
 | 
			
		||||
                this.hasDayError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            date.setMonth(month - 1)
 | 
			
		||||
 | 
			
		||||
			let day = parseInt(pieces[2])
 | 
			
		||||
			if (day < 1 || day > 32) {
 | 
			
		||||
				this.hasDayError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			date.setDate(day)
 | 
			
		||||
            let day = parseInt(pieces[2])
 | 
			
		||||
            if (day < 1 || day > 32) {
 | 
			
		||||
                this.hasDayError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            date.setDate(day)
 | 
			
		||||
 | 
			
		||||
			this.hasDayError = false
 | 
			
		||||
            this.hasDayError = false
 | 
			
		||||
 | 
			
		||||
			// hour
 | 
			
		||||
			if (!/^\d+$/.test(this.hour)) {
 | 
			
		||||
				this.hasHourError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			let hour = parseInt(this.hour)
 | 
			
		||||
			if (isNaN(hour)) {
 | 
			
		||||
				this.hasHourError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if (hour < 0 || hour >= 24) {
 | 
			
		||||
				this.hasHourError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			this.hasHourError = false
 | 
			
		||||
			date.setHours(hour)
 | 
			
		||||
            // hour
 | 
			
		||||
            if (!/^\d+$/.test(this.hour)) {
 | 
			
		||||
                this.hasHourError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            let hour = parseInt(this.hour)
 | 
			
		||||
            if (isNaN(hour)) {
 | 
			
		||||
                this.hasHourError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            if (hour < 0 || hour >= 24) {
 | 
			
		||||
                this.hasHourError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            this.hasHourError = false
 | 
			
		||||
            date.setHours(hour)
 | 
			
		||||
 | 
			
		||||
			// minute
 | 
			
		||||
			if (!/^\d+$/.test(this.minute)) {
 | 
			
		||||
				this.hasMinuteError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			let minute = parseInt(this.minute)
 | 
			
		||||
			if (isNaN(minute)) {
 | 
			
		||||
				this.hasMinuteError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if (minute < 0 || minute >= 60) {
 | 
			
		||||
				this.hasMinuteError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			this.hasMinuteError = false
 | 
			
		||||
			date.setMinutes(minute)
 | 
			
		||||
            // minute
 | 
			
		||||
            if (!/^\d+$/.test(this.minute)) {
 | 
			
		||||
                this.hasMinuteError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            let minute = parseInt(this.minute)
 | 
			
		||||
            if (isNaN(minute)) {
 | 
			
		||||
                this.hasMinuteError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            if (minute < 0 || minute >= 60) {
 | 
			
		||||
                this.hasMinuteError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            this.hasMinuteError = false
 | 
			
		||||
            date.setMinutes(minute)
 | 
			
		||||
 | 
			
		||||
			// second
 | 
			
		||||
			if (!/^\d+$/.test(this.second)) {
 | 
			
		||||
				this.hasSecondError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			let second = parseInt(this.second)
 | 
			
		||||
			if (isNaN(second)) {
 | 
			
		||||
				this.hasSecondError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if (second < 0 || second >= 60) {
 | 
			
		||||
				this.hasSecondError = true
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			this.hasSecondError = false
 | 
			
		||||
			date.setSeconds(second)
 | 
			
		||||
            // second
 | 
			
		||||
            if (!/^\d+$/.test(this.second)) {
 | 
			
		||||
                this.hasSecondError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            let second = parseInt(this.second)
 | 
			
		||||
            if (isNaN(second)) {
 | 
			
		||||
                this.hasSecondError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            if (second < 0 || second >= 60) {
 | 
			
		||||
                this.hasSecondError = true
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            this.hasSecondError = false
 | 
			
		||||
            date.setSeconds(second)
 | 
			
		||||
 | 
			
		||||
			this.timestamp = Math.ceil(date.getTime() / 1000)
 | 
			
		||||
		},
 | 
			
		||||
		leadingZero: function (s, l) {
 | 
			
		||||
			if (l <= s.length) {
 | 
			
		||||
				return s
 | 
			
		||||
			}
 | 
			
		||||
			for (let i = 0; i < l - s.length; i++) {
 | 
			
		||||
				s = "0" + s
 | 
			
		||||
			}
 | 
			
		||||
			return s
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	template: `<div>
 | 
			
		||||
            this.timestamp = parseInt(date.getTime() / 1000)
 | 
			
		||||
        },
 | 
			
		||||
        leadingZero: function (s, l) {
 | 
			
		||||
            if (l <= s.length) {
 | 
			
		||||
                return s
 | 
			
		||||
            }
 | 
			
		||||
            for (let i = 0; i < l - s.length; i++) {
 | 
			
		||||
                s = "0" + s
 | 
			
		||||
            }
 | 
			
		||||
            return s
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    template: `<div>
 | 
			
		||||
	<input type="hidden" :name="vName" :value="timestamp"/>
 | 
			
		||||
	<div class="ui fields inline" style="padding: 0; margin:0">
 | 
			
		||||
		<div class="ui field" :class="{error: hasDayError}">
 | 
			
		||||
			<input type="text" v-model="day" placeholder="YYYY-MM-DD" style="width:8em" maxlength="10" @input="change"/>
 | 
			
		||||
			<input type="text" v-model="day" placeholder="YYYY-MM-DD" style="width:8.6em" maxlength="10" @input="change" ref="dayInput"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="ui field" :class="{error: hasHourError}"><input type="text" v-model="hour" maxlength="2" style="width:4em" placeholder="时" @input="change"/></div>
 | 
			
		||||
		<div class="ui field">:</div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								web/public/js/components/iplist/ip-list-table.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								web/public/js/components/iplist/ip-list-table.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
Vue.component("ip-list-table", {
 | 
			
		||||
    props: ["v-items"],
 | 
			
		||||
    data: function () {
 | 
			
		||||
        return {
 | 
			
		||||
            items: this.vItems
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        updateItem: function (itemId) {
 | 
			
		||||
            this.$emit("update-item", itemId)
 | 
			
		||||
        },
 | 
			
		||||
        deleteItem: function (itemId) {
 | 
			
		||||
            this.$emit("delete-item", itemId)
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    template: `<div>
 | 
			
		||||
 <table class="ui table selectable celled" v-if="items.length > 0">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th style="width:18em">IP</th>
 | 
			
		||||
                <th>类型</th>
 | 
			
		||||
                <th>过期时间</th>
 | 
			
		||||
                <th>备注</th>
 | 
			
		||||
                <th class="two op">操作</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tr v-for="item in items">
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.type != 'all'">{{item.ipFrom}}<span v-if="item.ipTo.length > 0"> - {{item.ipTo}}</span></span>
 | 
			
		||||
                <span v-else class="disabled">*</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.type.length == 0">IPv4</span>
 | 
			
		||||
                <span v-else-if="item.type == 'ipv4'">IPv4</span>
 | 
			
		||||
                <span v-else-if="item.type == 'ipv6'">IPv6</span>
 | 
			
		||||
                <span v-else-if="item.type == 'all'"><strong>所有IP</strong></span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <div v-if="item.expiredTime.length > 0">
 | 
			
		||||
                    {{item.expiredTime}}
 | 
			
		||||
                    <div v-if="item.isExpired" style="margin-top: 0.5em">
 | 
			
		||||
                        <span class="ui label tiny basic red">已过期</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <span v-else class="disabled">不过期</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.reason.length > 0">{{item.reason}}</span>
 | 
			
		||||
                <span v-else class="disabled">-</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <a href="" @click.prevent="updateItem(item.id)">修改</a>  
 | 
			
		||||
                <a href="" @click.prevent="deleteItem(item.id)">删除</a>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>`
 | 
			
		||||
})
 | 
			
		||||
@@ -50,7 +50,41 @@ window.teaweb = {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    loadJS: function (file, callback) {
 | 
			
		||||
        let element = document.createElement("script")
 | 
			
		||||
        element.setAttribute("type", "text/javascript")
 | 
			
		||||
        element.setAttribute("src", file)
 | 
			
		||||
        if (typeof callback == "function") {
 | 
			
		||||
            element.addEventListener("load", callback)
 | 
			
		||||
        }
 | 
			
		||||
        document.head.append(element)
 | 
			
		||||
    },
 | 
			
		||||
    loadCSS: function (file, callback) {
 | 
			
		||||
        let element = document.createElement("link")
 | 
			
		||||
        element.setAttribute("rel", "stylesheet")
 | 
			
		||||
        element.setAttribute("type", "text/css")
 | 
			
		||||
        element.setAttribute("href", file)
 | 
			
		||||
        if (typeof callback == "function") {
 | 
			
		||||
            element.addEventListener("load", callback)
 | 
			
		||||
        }
 | 
			
		||||
        document.head.append(element)
 | 
			
		||||
    },
 | 
			
		||||
    datepicker: function (element, callback) {
 | 
			
		||||
        // 加载
 | 
			
		||||
        if (typeof Pikaday == "undefined") {
 | 
			
		||||
            let that = this
 | 
			
		||||
            this.loadJS("/js/moment.min.js")
 | 
			
		||||
            this.loadJS("/js/pikaday.js", function () {
 | 
			
		||||
                that.datepicker(element, callback)
 | 
			
		||||
            })
 | 
			
		||||
            this.loadCSS("/js/pikaday.css")
 | 
			
		||||
            this.loadCSS("/js/pikaday.theme.css")
 | 
			
		||||
            this.loadCSS("/js/pikaday.triangle.css")
 | 
			
		||||
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (typeof (element) == "string") {
 | 
			
		||||
            element = document.getElementById(element);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,19 +8,47 @@
 | 
			
		||||
	<input type="hidden" name="listId" :value="listId"/>
 | 
			
		||||
	<csrf-token></csrf-token>
 | 
			
		||||
	<table class="ui table definition selectable">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td class="title">开始IP *</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus"/>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>结束IP</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x"/>
 | 
			
		||||
				<p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">类型 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <select class="ui dropdown auto-width" name="type" v-model="type">
 | 
			
		||||
                    <option value="ipv4">IPv4</option>
 | 
			
		||||
                    <option value="ipv6">IPv6</option>
 | 
			
		||||
                    <option value="all">所有IP</option>
 | 
			
		||||
                </select>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv4'">单个IPv4或一个IPv4范围。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv6'">单个IPv6。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'all'">允许或禁用所有的IP。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv4 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv4'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>开始IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus"/>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>结束IP</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x"/>
 | 
			
		||||
                    <p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv6 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv6'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x:x:x:x:x:x:x:x" ref="focus"/>
 | 
			
		||||
                    <p class="comment">IPv6地址,比如 1406:3c00:0:2409:13:58:103:15</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td colspan="2"><more-options-indicator></more-options-indicator></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
    this.type = "ipv4"
 | 
			
		||||
})
 | 
			
		||||
@@ -5,30 +5,6 @@
 | 
			
		||||
 | 
			
		||||
	<p class="comment" v-if="items.length == 0">暂时还没有IP。</p>
 | 
			
		||||
 | 
			
		||||
	<table class="ui table selectable celled" v-if="items.length > 0">
 | 
			
		||||
		<thead>
 | 
			
		||||
			<tr>
 | 
			
		||||
				<th>IP</th>
 | 
			
		||||
				<th>过期时间</th>
 | 
			
		||||
				<th>备注</th>
 | 
			
		||||
				<th class="two op">操作</th>
 | 
			
		||||
			</tr>
 | 
			
		||||
		</thead>
 | 
			
		||||
		<tr v-for="item in items">
 | 
			
		||||
			<td>{{item.ipFrom}}<span v-if="item.ipTo.length > 0"> - {{item.ipTo}}</span></td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<span v-if="item.expiredTime.length > 0">{{item.expiredTime}}</span>
 | 
			
		||||
				<span v-else class="disabled">不过期</span>
 | 
			
		||||
			</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<span v-if="item.reason.length > 0">{{item.reason}}</span>
 | 
			
		||||
				<span v-else class="disabled">-</span>
 | 
			
		||||
			</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<a href="" @click.prevent="updateItem(item.id)">修改</a>  
 | 
			
		||||
				<a href="" @click.prevent="deleteItem(item.id)">删除</a>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
	</table>
 | 
			
		||||
    <ip-list-table v-if="items.length > 0" :v-items="items" @update-item="updateItem" @delete-item="deleteItem"></ip-list-table>
 | 
			
		||||
 | 
			
		||||
	<div class="page" v-html="page"></div>
 | 
			
		||||
@@ -5,21 +5,50 @@
 | 
			
		||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
 | 
			
		||||
	<input type="hidden" name="firewallPolicyId" :value="firewallPolicyId"/>
 | 
			
		||||
	<input type="hidden" name="itemId" :value="item.id"/>
 | 
			
		||||
    <input type="hidden" name="type" :value="item.type"/>
 | 
			
		||||
	<csrf-token></csrf-token>
 | 
			
		||||
	<table class="ui table definition selectable">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td class="title">开始IP *</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>结束IP</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x" v-model="item.ipTo"/>
 | 
			
		||||
				<p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">类型 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <!-- 类型不允许修改 -->
 | 
			
		||||
                <span v-if="item.type == 'ipv4'">IPv4</span>
 | 
			
		||||
                <span v-if="item.type == 'ipv6'">IPv6</span>
 | 
			
		||||
                <span v-if="item.type == 'all'">所有IP</span>
 | 
			
		||||
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv4'">单个IPv4或一个IPv4范围。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv6'">单个IPv6。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'all'">允许或禁用所有的IP。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv4 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv4'">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>开始IP *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>结束IP</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x" v-model="item.ipTo"/>
 | 
			
		||||
                <p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv6 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv6'">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>IP *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <input type="text" name="ipFrom" maxlength="64" placeholder="x:x:x:x:x:x:x:x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
                <p class="comment">IPv6地址,比如 1406:3c00:0:2409:13:58:103:15</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td colspan="2"><more-options-indicator></more-options-indicator></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@
 | 
			
		||||
		<a href="" class="item" @click.prevent="createGroup(type)">[添加分组]</a>
 | 
			
		||||
	</second-menu>
 | 
			
		||||
 | 
			
		||||
    <p class="ui message warning" v-if="!wafIsOn">当前WAF未启用,设置将在<a :href="'/servers/server/settings/waf?serverId=' + serverId">[启用]</a>后生效。</p>
 | 
			
		||||
 | 
			
		||||
	<p class="comment" v-if="groups.length == 0">暂时还没有规则分组。</p>
 | 
			
		||||
 | 
			
		||||
	<table class="ui table selectable celled" v-if="groups.length > 0" id="sortable-table">
 | 
			
		||||
 
 | 
			
		||||
@@ -13,33 +13,10 @@
 | 
			
		||||
        <span class="item">ID: {{listId}}   <tip-icon content="ID可以用于使用API操作此IP名单"></tip-icon></span>
 | 
			
		||||
    </second-menu>
 | 
			
		||||
 | 
			
		||||
    <p class="comment" v-if="items.length == 0">暂时还没有IP。</p>
 | 
			
		||||
    <p class="ui message warning" v-if="!wafIsOn">当前WAF未启用,设置将在<a :href="'/servers/server/settings/waf?serverId=' + serverId">[启用]</a>后生效。</p>
 | 
			
		||||
 | 
			
		||||
    <table class="ui table selectable celled" v-if="items.length > 0">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th>IP</th>
 | 
			
		||||
                <th>过期时间</th>
 | 
			
		||||
                <th>备注</th>
 | 
			
		||||
                <th class="two op">操作</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tr v-for="item in items">
 | 
			
		||||
            <td>{{item.ipFrom}}<span v-if="item.ipTo.length > 0"> - {{item.ipTo}}</span></td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.expiredTime.length > 0">{{item.expiredTime}}</span>
 | 
			
		||||
                <span v-else class="disabled">不过期</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.reason.length > 0">{{item.reason}}</span>
 | 
			
		||||
                <span v-else class="disabled">-</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <a href="" @click.prevent="updateItem(item.id)">修改</a>  
 | 
			
		||||
                <a href="" @click.prevent="deleteItem(item.id)">删除</a>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <p class="comment" v-if="items.length == 0">暂时还没有IP。</p>
 | 
			
		||||
    <ip-list-table v-if="items.length > 0" :v-items="items" @update-item="updateItem" @delete-item="deleteItem"></ip-list-table>
 | 
			
		||||
 | 
			
		||||
    <div class="page" v-html="page"></div>
 | 
			
		||||
    {$end}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.updateItem = function (itemId) {
 | 
			
		||||
		teaweb.popup(Tea.url(".updateIPPopup?listId=" + this.listId, {itemId: itemId}), {
 | 
			
		||||
			height: "23em",
 | 
			
		||||
			height: "24em",
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				teaweb.success("保存成功", function () {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
@@ -27,7 +27,7 @@ Tea.context(function () {
 | 
			
		||||
	 */
 | 
			
		||||
	this.createIP = function (type) {
 | 
			
		||||
		teaweb.popup("/servers/server/settings/waf/ipadmin/createIPPopup?listId=" + this.listId + '&type=' + type, {
 | 
			
		||||
			height: "23em",
 | 
			
		||||
			height: "24em",
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				window.location.reload()
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,54 @@
 | 
			
		||||
{$layout "layout_popup"}
 | 
			
		||||
 | 
			
		||||
<h3 v-if="type == 'white'">添加IP到白名单</h3>
 | 
			
		||||
<h3 v-if="type == 'black'">添加IP到黑名单</h3>
 | 
			
		||||
<h3 v-if="listType == 'white'">添加IP到白名单</h3>
 | 
			
		||||
<h3 v-if="listType == 'black'">添加IP到黑名单</h3>
 | 
			
		||||
 | 
			
		||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
 | 
			
		||||
	<input type="hidden" name="listId" :value="listId"/>
 | 
			
		||||
	<csrf-token></csrf-token>
 | 
			
		||||
	<table class="ui table definition selectable">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td class="title">开始IP *</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus"/>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>结束IP</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x"/>
 | 
			
		||||
				<p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">类型 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <select class="ui dropdown auto-width" name="type" v-model="type">
 | 
			
		||||
                    <option value="ipv4">IPv4</option>
 | 
			
		||||
                    <option value="ipv6">IPv6</option>
 | 
			
		||||
                    <option value="all">所有IP</option>
 | 
			
		||||
                </select>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv4'">单个IPv4或一个IPv4范围。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv6'">单个IPv6。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'all'">允许或禁用所有的IP。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv4 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv4'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>开始IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus"/>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>结束IP</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x"/>
 | 
			
		||||
                    <p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv6 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv6'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x:x:x:x:x:x:x:x" ref="focus"/>
 | 
			
		||||
                    <p class="comment">IPv6地址,比如 1406:3c00:0:2409:13:58:103:15</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td colspan="2"><more-options-indicator></more-options-indicator></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
@@ -37,6 +66,6 @@
 | 
			
		||||
			</tr>
 | 
			
		||||
		</tbody>
 | 
			
		||||
	</table>
 | 
			
		||||
    <p class="comment">添加后将会在5分钟后生效。</p>
 | 
			
		||||
    <p class="comment">添加后将会在5分钟内生效。</p>
 | 
			
		||||
	<submit-btn></submit-btn>
 | 
			
		||||
</form>
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
    this.type = "ipv4"
 | 
			
		||||
})
 | 
			
		||||
@@ -13,33 +13,10 @@
 | 
			
		||||
        <span class="item">ID: {{listId}}   <tip-icon content="ID可以用于使用API操作此IP名单"></tip-icon></span>
 | 
			
		||||
    </second-menu>
 | 
			
		||||
 | 
			
		||||
    <p class="comment" v-if="items.length == 0">暂时还没有IP。</p>
 | 
			
		||||
    <p class="ui message warning" v-if="!wafIsOn">当前WAF未启用,设置将在<a :href="'/servers/server/settings/waf?serverId=' + serverId">[启用]</a>后生效。</p>
 | 
			
		||||
 | 
			
		||||
    <table class="ui table selectable celled" v-if="items.length > 0">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th>IP</th>
 | 
			
		||||
                <th>过期时间</th>
 | 
			
		||||
                <th>备注</th>
 | 
			
		||||
                <th class="two op">操作</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tr v-for="item in items">
 | 
			
		||||
            <td>{{item.ipFrom}}<span v-if="item.ipTo.length > 0"> - {{item.ipTo}}</span></td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.expiredTime.length > 0">{{item.expiredTime}}</span>
 | 
			
		||||
                <span v-else class="disabled">不过期</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <span v-if="item.reason.length > 0">{{item.reason}}</span>
 | 
			
		||||
                <span v-else class="disabled">-</span>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <a href="" @click.prevent="updateItem(item.id)">修改</a>  
 | 
			
		||||
                <a href="" @click.prevent="deleteItem(item.id)">删除</a>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <p class="comment" v-if="items.length == 0">暂时还没有IP。</p>
 | 
			
		||||
    <ip-list-table v-if="items.length > 0" :v-items="items" @update-item="updateItem" @delete-item="deleteItem"></ip-list-table>
 | 
			
		||||
 | 
			
		||||
    <div class="page" v-html="page"></div>
 | 
			
		||||
    {$end}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.updateItem = function (itemId) {
 | 
			
		||||
		teaweb.popup(Tea.url(".updateIPPopup?listId=" + this.listId, {itemId: itemId}), {
 | 
			
		||||
			height: "23em",
 | 
			
		||||
			height: "24em",
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				teaweb.success("保存成功", function () {
 | 
			
		||||
					teaweb.reload()
 | 
			
		||||
@@ -27,7 +27,7 @@ Tea.context(function () {
 | 
			
		||||
	 */
 | 
			
		||||
	this.createIP = function (type) {
 | 
			
		||||
		teaweb.popup("/servers/server/settings/waf/ipadmin/createIPPopup?listId=" + this.listId + '&type=' + type, {
 | 
			
		||||
			height: "23em",
 | 
			
		||||
			height: "24em",
 | 
			
		||||
			callback: function () {
 | 
			
		||||
				window.location.reload()
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,21 +4,51 @@
 | 
			
		||||
 | 
			
		||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
 | 
			
		||||
	<input type="hidden" name="itemId" :value="item.id"/>
 | 
			
		||||
    <input type="hidden" name="type" :value="item.type"/>
 | 
			
		||||
	<csrf-token></csrf-token>
 | 
			
		||||
	<table class="ui table definition selectable">
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td class="title">开始IP *</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td>结束IP</td>
 | 
			
		||||
			<td>
 | 
			
		||||
				<input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x" v-model="item.ipTo"/>
 | 
			
		||||
				<p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
			</td>
 | 
			
		||||
		</tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="title">类型 *</td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <!-- 类型不允许修改 -->
 | 
			
		||||
                <span v-if="item.type == 'ipv4'">IPv4</span>
 | 
			
		||||
                <span v-if="item.type == 'ipv6'">IPv6</span>
 | 
			
		||||
                <span v-if="item.type == 'all'">所有IP</span>
 | 
			
		||||
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv4'">单个IPv4或一个IPv4范围。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'ipv6'">单个IPv6。</p>
 | 
			
		||||
                <p class="comment" v-if="type == 'all'">允许或禁用所有的IP。</p>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv4 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv4'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>开始IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x.x.x.x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>结束IP</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipTo" maxlength="64" placeholder="x.x.x.x" v-model="item.ipTo"/>
 | 
			
		||||
                    <p class="comment">表示IP段的时候需要填写此项。</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
        <!-- IPv6 -->
 | 
			
		||||
        <tbody v-if="type == 'ipv6'">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>IP *</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" name="ipFrom" maxlength="64" placeholder="x:x:x:x:x:x:x:x" ref="focus" v-model="item.ipFrom"/>
 | 
			
		||||
                    <p class="comment">IPv6地址,比如 1406:3c00:0:2409:13:58:103:15</p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
 | 
			
		||||
		<tr>
 | 
			
		||||
			<td colspan="2"><more-options-indicator></more-options-indicator></td>
 | 
			
		||||
		</tr>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user