diff --git a/internal/utils/ip_utils.go b/internal/utils/ip_utils.go index 58105208..ebb7fcb4 100644 --- a/internal/utils/ip_utils.go +++ b/internal/utils/ip_utils.go @@ -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 } diff --git a/internal/utils/ip_utils_test.go b/internal/utils/ip_utils_test.go new file mode 100644 index 00000000..8c891ce9 --- /dev/null +++ b/internal/utils/ip_utils_test.go @@ -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) + } + } +} diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go b/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go index fcef1be7..cd7b69de 100644 --- a/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go +++ b/internal/web/actions/default/servers/components/waf/ipadmin/createIPPopup.go @@ -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) diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/lists.go b/internal/web/actions/default/servers/components/waf/ipadmin/lists.go index 3d7d5bd6..65121840 100644 --- a/internal/web/actions/default/servers/components/waf/ipadmin/lists.go +++ b/internal/web/actions/default/servers/components/waf/ipadmin/lists.go @@ -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 diff --git a/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go b/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go index 525d4b62..243eaf6c 100644 --- a/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go +++ b/internal/web/actions/default/servers/components/waf/ipadmin/updateIPPopup.go @@ -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) diff --git a/internal/web/actions/default/servers/server/settings/waf/groups.go b/internal/web/actions/default/servers/server/settings/waf/groups.go index f6c21d92..565ad5cf 100644 --- a/internal/web/actions/default/servers/server/settings/waf/groups.go +++ b/internal/web/actions/default/servers/server/settings/waf/groups.go @@ -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() } diff --git a/internal/web/actions/default/servers/server/settings/waf/ipadmin/allowList.go b/internal/web/actions/default/servers/server/settings/waf/ipadmin/allowList.go index f52c227d..215b24b3 100644 --- a/internal/web/actions/default/servers/server/settings/waf/ipadmin/allowList.go +++ b/internal/web/actions/default/servers/server/settings/waf/ipadmin/allowList.go @@ -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() } diff --git a/internal/web/actions/default/servers/server/settings/waf/ipadmin/createIPPopup.go b/internal/web/actions/default/servers/server/settings/waf/ipadmin/createIPPopup.go index 985bb4cc..9b8f1206 100644 --- a/internal/web/actions/default/servers/server/settings/waf/ipadmin/createIPPopup.go +++ b/internal/web/actions/default/servers/server/settings/waf/ipadmin/createIPPopup.go @@ -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) diff --git a/internal/web/actions/default/servers/server/settings/waf/ipadmin/denyList.go b/internal/web/actions/default/servers/server/settings/waf/ipadmin/denyList.go index e22f9a07..f4701f63 100644 --- a/internal/web/actions/default/servers/server/settings/waf/ipadmin/denyList.go +++ b/internal/web/actions/default/servers/server/settings/waf/ipadmin/denyList.go @@ -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() } diff --git a/internal/web/actions/default/servers/server/settings/waf/ipadmin/updateIPPopup.go b/internal/web/actions/default/servers/server/settings/waf/ipadmin/updateIPPopup.go index f25d3d9c..7b85cd6e 100644 --- a/internal/web/actions/default/servers/server/settings/waf/ipadmin/updateIPPopup.go +++ b/internal/web/actions/default/servers/server/settings/waf/ipadmin/updateIPPopup.go @@ -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) diff --git a/web/public/js/components/common/datetime-input.js b/web/public/js/components/common/datetime-input.js index 6cfb63fb..cf565236 100644 --- a/web/public/js/components/common/datetime-input.js +++ b/web/public/js/components/common/datetime-input.js @@ -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: `
| IP | +类型 | +过期时间 | +备注 | +操作 | +
|---|---|---|---|---|
| + {{item.ipFrom}} - {{item.ipTo}} + * + | ++ IPv4 + IPv4 + IPv6 + 所有IP + | +
+
+ {{item.expiredTime}}
+
+ 不过期
+
+ 已过期
+
+ |
+ + {{item.reason}} + - + | ++ 修改 + 删除 + | +
| 开始IP * | -- - | -||
| 结束IP | -
-
- 表示IP段的时候需要填写此项。 - |
- ||
| 类型 * | +
+
+ 单个IPv4或一个IPv4范围。 +单个IPv6。 +允许或禁用所有的IP。 + |
+ ||
| 开始IP * | ++ + | +||
| 结束IP | +
+
+ 表示IP段的时候需要填写此项。 + |
+ ||
| IP * | +
+
+ IPv6地址,比如 1406:3c00:0:2409:13:58:103:15 + |
+ ||
| IP | -过期时间 | -备注 | -操作 | -
|---|---|---|---|
| {{item.ipFrom}} - {{item.ipTo}} | -- {{item.expiredTime}} - 不过期 - | -- {{item.reason}} - - - | -- 修改 - 删除 - | -