diff --git a/internal/rpc/rpc_client_test.go b/internal/rpc/rpc_client_test.go index 1fa59d64..d0cb2361 100644 --- a/internal/rpc/rpc_client_test.go +++ b/internal/rpc/rpc_client_test.go @@ -18,7 +18,7 @@ func TestRPCClient_NodeRPC(t *testing.T) { if err != nil { t.Fatal(err) } - rpc, err := NewRPCClient(config) + rpc, err := NewRPCClient(config, true) if err != nil { t.Fatal(err) } @@ -41,7 +41,7 @@ func TestRPC_Dial_HTTP(t *testing.T) { }, NodeId: "a7e55782dab39bce0901058a1e14a0e6", Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y", - }) + }, true) if err != nil { t.Fatal(err) } @@ -62,7 +62,7 @@ func TestRPC_Dial_HTTP_2(t *testing.T) { }, NodeId: "a7e55782dab39bce0901058a1e14a0e6", Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y", - }) + }, true) if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func TestRPC_Dial_HTTPS(t *testing.T) { }, NodeId: "a7e55782dab39bce0901058a1e14a0e6", Secret: "lvyPobI3BszkJopz5nPTocOs0OLkEJ7y", - }) + }, true) if err != nil { t.Fatal(err) } diff --git a/internal/utils/numberutils/utils.go b/internal/utils/numberutils/utils.go index 71bfd4ed..ee3e5618 100644 --- a/internal/utils/numberutils/utils.go +++ b/internal/utils/numberutils/utils.go @@ -13,6 +13,16 @@ func FormatInt(value int) string { return strconv.Itoa(value) } +func Pow1024(n int) int64 { + if n <= 0 { + return 1 + } + if n == 1 { + return 1024 + } + return Pow1024(n-1) * 1024 +} + func FormatBytes(bytes int64) string { if bytes < 1024 { return FormatInt64(bytes) + "B" diff --git a/internal/web/actions/default/servers/server/settings/requestLimit/index.go b/internal/web/actions/default/servers/server/settings/requestLimit/index.go new file mode 100644 index 00000000..4e60fa7c --- /dev/null +++ b/internal/web/actions/default/servers/server/settings/requestLimit/index.go @@ -0,0 +1,57 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package requestlimit + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/actions" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "setting", "index") + this.SecondMenu("requestLimit") +} + +func (this *IndexAction) RunGet(params struct { + ServerId int64 +}) { + this.Data["serverId"] = params.ServerId + + webConfig, err := dao.SharedHTTPWebDAO.FindWebConfigWithServerId(this.AdminContext(), params.ServerId) + if err != nil { + this.ErrorPage(err) + return + } + + this.Data["webId"] = webConfig.Id + this.Data["requestLimitConfig"] = webConfig.RequestLimit + + this.Show() +} + +func (this *IndexAction) RunPost(params struct { + WebId int64 + RequestLimitJSON []byte + + Must *actions.Must + CSRF *actionutils.CSRF +}) { + defer this.CreateLogInfo("修改Web %d 请求限制", params.WebId) + + _, err := this.RPC().HTTPWebRPC().UpdateHTTPWebRequestLimit(this.AdminContext(), &pb.UpdateHTTPWebRequestLimitRequest{ + HttpWebId: params.WebId, + RequestLimitJSON: params.RequestLimitJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + + this.Success() +} diff --git a/internal/web/actions/default/servers/server/settings/requestLimit/init.go b/internal/web/actions/default/servers/server/settings/requestLimit/init.go new file mode 100644 index 00000000..dbd09811 --- /dev/null +++ b/internal/web/actions/default/servers/server/settings/requestLimit/init.go @@ -0,0 +1,19 @@ +package requestlimit + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/serverutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers" + "github.com/iwind/TeaGo" +) + +func init() { + TeaGo.BeforeStart(func(server *TeaGo.Server) { + server. + Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeServer)). + Helper(serverutils.NewServerHelper()). + Prefix("/servers/server/settings/requestLimit"). + GetPost("", new(IndexAction)). + EndAll() + }) +} diff --git a/internal/web/actions/default/servers/serverutils/server_helper.go b/internal/web/actions/default/servers/serverutils/server_helper.go index a05e8429..427e164b 100644 --- a/internal/web/actions/default/servers/serverutils/server_helper.go +++ b/internal/web/actions/default/servers/serverutils/server_helper.go @@ -125,7 +125,17 @@ func (this *ServerHelper) createLeftMenu(action *actions.ActionObject) { case "stat": action.Data["leftMenuItems"] = this.createStatMenu(types.String(secondMenuItem), serverIdString, serverConfig) case "setting": - action.Data["leftMenuItems"] = this.createSettingsMenu(types.String(secondMenuItem), serverIdString, serverConfig) + var menuItems = this.createSettingsMenu(types.String(secondMenuItem), serverIdString, serverConfig) + action.Data["leftMenuItems"] = menuItems + + // 当前菜单 + action.Data["leftMenuActiveItem"] = nil + for _, item := range menuItems { + if item.GetBool("isActive") { + action.Data["leftMenuActiveItem"] = item + break + } + } case "delete": action.Data["leftMenuItems"] = this.createDeleteMenu(types.String(secondMenuItem), serverIdString, serverConfig) } @@ -354,6 +364,13 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri "isOn": serverConfig.Web != nil && serverConfig.Web.RemoteAddr != nil && serverConfig.Web.RemoteAddr.IsOn, }) + menuItems = append(menuItems, maps.Map{ + "name": "请求限制", + "url": "/servers/server/settings/requestLimit?serverId=" + serverIdString, + "isActive": secondMenuItem == "requestLimit", + "isOn": serverConfig.Web != nil && serverConfig.Web.RequestLimit != nil && serverConfig.Web.RequestLimit.IsOn, + }) + if teaconst.IsPlus { menuItems = append(menuItems, maps.Map{ "name": "流量限制", diff --git a/internal/web/import.go b/internal/web/import.go index 9f3f6778..48afff1e 100644 --- a/internal/web/import.go +++ b/internal/web/import.go @@ -88,6 +88,7 @@ import ( _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/pages" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/redirects" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/remoteAddr" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/requestLimit" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/reverseProxy" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/rewrite" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/servers/server/settings/serverNames" diff --git a/web/public/js/components.js b/web/public/js/components.js index e2ea7658..b43a9e78 100755 --- a/web/public/js/components.js +++ b/web/public/js/components.js @@ -2723,6 +2723,105 @@ Vue.component("http-cache-ref-box", { ` }) +// 请求限制 +Vue.component("http-request-limit-config-box", { + props: ["v-request-limit-config"], + data: function () { + let config = this.vRequestLimitConfig + if (config == null) { + config = { + isPrior: false, + isOn: false, + maxConns: 0, + maxConnsPerIP: 0, + maxBodySize: { + count: -1, + unit: "kb" + }, + outBandwidthPerConn: { + count: -1, + unit: "kb" + } + } + } + return { + config: config, + maxConns: config.maxConns, + maxConnsPerIP: config.maxConnsPerIP + } + }, + watch: { + maxConns: function (v) { + let conns = parseInt(v, 10) + if (isNaN(conns)) { + this.config.maxConns = 0 + return + } + if (conns < 0) { + this.config.maxConns = 0 + } else { + this.config.maxConns = conns + } + }, + maxConnsPerIP: function (v) { + let conns = parseInt(v, 10) + if (isNaN(conns)) { + this.config.maxConnsPerIP = 0 + return + } + if (conns < 0) { + this.config.maxConnsPerIP = 0 + } else { + this.config.maxConnsPerIP = conns + } + } + }, + template: `
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
是否启用 + +
最大并发连接数 + +

为0表示不限制。

+
单IP最大并发连接数 + +

为0表示不限制。

+
单连接带宽限制 + +

客户端单个请求每秒可以读取的下行流量。

+
单请求最大尺寸 + +

单个请求能发送的最大内容尺寸。

+
+
+
` +}) + // 浏览条件列表 Vue.component("http-request-conds-view", { props: ["v-conds"], @@ -5976,7 +6075,7 @@ Vue.component("http-pages-and-shutdown-box", { -

开启临时关闭页面时,所有请求的响应都会显示此页面。可用于临时升级网站使用。

+

开启临时关闭页面时,所有请求都会直接显示此页面。可用于临时升级网站或者禁止用户访问某个网页。

@@ -6428,6 +6527,7 @@ Vue.component("http-expires-time-config-box", { 强制缓存时间 +

从客户端访问的时间开始要缓存的时长。

@@ -11108,7 +11208,7 @@ Vue.component("source-code-box", { }) Vue.component("size-capacity-box", { - props: ["v-name", "v-value", "v-count", "v-unit", "size", "maxlength"], + props: ["v-name", "v-value", "v-count", "v-unit", "size", "maxlength", "v-supported-units"], data: function () { let v = this.vValue if (v == null) { @@ -11131,11 +11231,17 @@ Vue.component("size-capacity-box", { vMaxlength = 10 } + let supportedUnits = this.vSupportedUnits + if (supportedUnits == null) { + supportedUnits = [] + } + return { capacity: v, countString: (v.count >= 0) ? v.count.toString() : "", vSize: vSize, - vMaxlength: vMaxlength + vMaxlength: vMaxlength, + supportedUnits: supportedUnits } }, watch: { @@ -11165,12 +11271,13 @@ Vue.component("size-capacity-box", {
` diff --git a/web/public/js/components/common/size-capacity-box.js b/web/public/js/components/common/size-capacity-box.js index af571081..db123b68 100644 --- a/web/public/js/components/common/size-capacity-box.js +++ b/web/public/js/components/common/size-capacity-box.js @@ -1,5 +1,5 @@ Vue.component("size-capacity-box", { - props: ["v-name", "v-value", "v-count", "v-unit", "size", "maxlength"], + props: ["v-name", "v-value", "v-count", "v-unit", "size", "maxlength", "v-supported-units"], data: function () { let v = this.vValue if (v == null) { @@ -22,11 +22,17 @@ Vue.component("size-capacity-box", { vMaxlength = 10 } + let supportedUnits = this.vSupportedUnits + if (supportedUnits == null) { + supportedUnits = [] + } + return { capacity: v, countString: (v.count >= 0) ? v.count.toString() : "", vSize: vSize, - vMaxlength: vMaxlength + vMaxlength: vMaxlength, + supportedUnits: supportedUnits } }, watch: { @@ -56,12 +62,13 @@ Vue.component("size-capacity-box", {
` diff --git a/web/public/js/components/server/http-pages-and-shutdown-box.js b/web/public/js/components/server/http-pages-and-shutdown-box.js index 7ea98255..8f68e1ee 100644 --- a/web/public/js/components/server/http-pages-and-shutdown-box.js +++ b/web/public/js/components/server/http-pages-and-shutdown-box.js @@ -153,7 +153,7 @@ Vue.component("http-pages-and-shutdown-box", { -

开启临时关闭页面时,所有请求的响应都会显示此页面。可用于临时升级网站使用。

+

开启临时关闭页面时,所有请求都会直接显示此页面。可用于临时升级网站或者禁止用户访问某个网页。

diff --git a/web/public/js/components/server/http-request-limit-config-box.js b/web/public/js/components/server/http-request-limit-config-box.js new file mode 100644 index 00000000..1f8813ea --- /dev/null +++ b/web/public/js/components/server/http-request-limit-config-box.js @@ -0,0 +1,98 @@ +// 请求限制 +Vue.component("http-request-limit-config-box", { + props: ["v-request-limit-config"], + data: function () { + let config = this.vRequestLimitConfig + if (config == null) { + config = { + isPrior: false, + isOn: false, + maxConns: 0, + maxConnsPerIP: 0, + maxBodySize: { + count: -1, + unit: "kb" + }, + outBandwidthPerConn: { + count: -1, + unit: "kb" + } + } + } + return { + config: config, + maxConns: config.maxConns, + maxConnsPerIP: config.maxConnsPerIP + } + }, + watch: { + maxConns: function (v) { + let conns = parseInt(v, 10) + if (isNaN(conns)) { + this.config.maxConns = 0 + return + } + if (conns < 0) { + this.config.maxConns = 0 + } else { + this.config.maxConns = conns + } + }, + maxConnsPerIP: function (v) { + let conns = parseInt(v, 10) + if (isNaN(conns)) { + this.config.maxConnsPerIP = 0 + return + } + if (conns < 0) { + this.config.maxConnsPerIP = 0 + } else { + this.config.maxConnsPerIP = conns + } + } + }, + template: `
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
是否启用 + +
最大并发连接数 + +

为0表示不限制。

+
单IP最大并发连接数 + +

为0表示不限制。

+
单连接带宽限制 + +

客户端单个请求每秒可以读取的下行流量。

+
单请求最大尺寸 + +

单个请求能发送的最大内容尺寸。

+
+
+
` +}) \ No newline at end of file diff --git a/web/public/js/utils.js b/web/public/js/utils.js index 8b563458..97b82991 100644 --- a/web/public/js/utils.js +++ b/web/public/js/utils.js @@ -116,22 +116,25 @@ window.teaweb = { formatBytes: function (bytes) { bytes = Math.ceil(bytes); - if (bytes < 1024) { + if (bytes < Math.pow(1024, 1)) { return bytes + "B"; } - if (bytes < 1024 * 1024) { - return (Math.round(bytes * 100 / 1024) / 100) + "K"; + if (bytes < Math.pow(1024, 2)) { + return (Math.round(bytes * 100 / Math.pow(1024, 1)) / 100) + "KB"; } - if (bytes < 1024 * 1024 * 1024) { - return (Math.round(bytes * 100 / 1024 / 1024) / 100) + "M"; + if (bytes < Math.pow(1024, 3)) { + return (Math.round(bytes * 100 / Math.pow(1024, 2)) / 100) + "MB"; } - if (bytes < 1024 * 1024 * 1024 * 1024) { - return (Math.round(bytes * 100 / 1024 / 1024 / 1024) / 100) + "G"; + if (bytes < Math.pow(1024, 4)) { + return (Math.round(bytes * 100 / Math.pow(1024, 3)) / 100) + "GB"; } - if (bytes < 1024 * 1024 * 1024 * 1024 * 1024) { - return (Math.round(bytes * 100 / 1024 / 1024 / 1024 / 1024) / 100) + "T"; + if (bytes < Math.pow(1024, 5)) { + return (Math.round(bytes * 100 / Math.pow(1024, 4)) / 100) + "TB"; } - return (Math.round(bytes * 100 / 1024 / 1024 / 1024 / 1024 / 1024) / 100) + "P"; + if (bytes < Math.pow(1024, 6)) { + return (Math.round(bytes * 100 / Math.pow(1024, 5)) / 100) + "PB"; + } + return (Math.round(bytes * 100 / Math.pow(1024, 6)) / 100) + "EB"; }, formatNumber: function (x) { if (x == null) { @@ -178,18 +181,24 @@ window.teaweb = { let max = Math.max.apply(this, stats.map(countFunc)) let divider = 1 let unit = "" - if (max >= 1024 * 1024 * 1024 * 1024) { - unit = "T" - divider = 1024 * 1024 * 1024 * 1024 - } else if (max >= 1024 * 1024 * 1024) { - unit = "G" - divider = 1024 * 1024 * 1024 - } else if (max >= 1024 * 1024) { - unit = "M" - divider = 1024 * 1024 - } else if (max >= 1024) { - unit = "K" - divider = 1024 + if (max >= Math.pow(1024, 6)) { + unit = "EB" + divider = Math.pow(1024, 6) + } else if (max >= Math.pow(1024, 5)) { + unit = "PB" + divider = Math.pow(1024, 5) + } else if (max >= Math.pow(1024, 4)) { + unit = "TB" + divider = Math.pow(1024, 4) + } else if (max >= Math.pow(1024, 3)) { + unit = "GB" + divider = Math.pow(1024, 3) + } else if (max >= Math.pow(1024, 2)) { + unit = "MB" + divider = Math.pow(1024, 2) + } else if (max >= Math.pow(1024, 1)) { + unit = "KB" + divider = Math.pow(1024, 1) } return { unit: unit, diff --git a/web/views/@default/servers/server/settings/@settings_menu.html b/web/views/@default/servers/server/settings/@settings_menu.html index 6b2e4593..1a2269ff 100644 --- a/web/views/@default/servers/server/settings/@settings_menu.html +++ b/web/views/@default/servers/server/settings/@settings_menu.html @@ -1,8 +1,10 @@ 服务列表 | - "{{server.name}}"设置 - | + "{{server.name}}"设置 + » + "{{leftMenuActiveItem.name}}"设置 + | diff --git a/web/views/@default/servers/server/settings/locations/create.js b/web/views/@default/servers/server/settings/locations/create.js index a3bc366e..14856c2a 100644 --- a/web/views/@default/servers/server/settings/locations/create.js +++ b/web/views/@default/servers/server/settings/locations/create.js @@ -2,7 +2,7 @@ Tea.context(function () { this.success = NotifySuccess("添加成功", "/servers/server/settings/locations?serverId=" + this.serverId) this.type = 1 - this.selectedType = null + this.selectedType = this.patternTypes[0] this.changePatternType = function (type) { diff --git a/web/views/@default/servers/server/settings/requestLimit/index.html b/web/views/@default/servers/server/settings/requestLimit/index.html new file mode 100644 index 00000000..9ed8dced --- /dev/null +++ b/web/views/@default/servers/server/settings/requestLimit/index.html @@ -0,0 +1,14 @@ +{$layout} +{$template "../settings_menu"} +{$template "/left_menu_with_menu"} + +
+
+ + + + + + +
+
\ No newline at end of file diff --git a/web/views/@default/servers/server/settings/requestLimit/index.js b/web/views/@default/servers/server/settings/requestLimit/index.js new file mode 100644 index 00000000..295a9aaf --- /dev/null +++ b/web/views/@default/servers/server/settings/requestLimit/index.js @@ -0,0 +1,3 @@ +Tea.context(function () { + this.success = NotifyReloadSuccess("保存成功") +}) \ No newline at end of file