diff --git a/build/build.sh b/build/build.sh index 820e26da..0e95e2ba 100755 --- a/build/build.sh +++ b/build/build.sh @@ -50,6 +50,10 @@ function build() { echo "==============================" cd - + # generate files + echo "generating files ..." + go run -tags $TAG $ROOT/../cmd/edge-admin/main.go generate + # create dir & copy files echo "copying ..." if [ ! -d $DIST ]; then @@ -83,10 +87,6 @@ function build() { rm -f $(basename $EDGE_API_ZIP_FILE) cd - - # generate files - echo "generating files ..." - go run -tags $TAG $ROOT/../cmd/edge-admin/main.go generate - # build echo "building "${NAME}" ..." env GOOS=$OS GOARCH=$ARCH go build -tags $TAG -ldflags="-s -w" -o $DIST/bin/${NAME} $ROOT/../cmd/edge-admin/main.go diff --git a/web/public/js/components.js b/web/public/js/components.js index 08201744..45003181 100755 --- a/web/public/js/components.js +++ b/web/public/js/components.js @@ -57,10 +57,24 @@ Vue.component("traffic-map-box", { areaColor: "#E9F0F9", borderColor: "#DDD" }, + label: { + show: false, + fontSize: "10px", + color: "#fff", + backgroundColor: "#8B9BD3", + padding: [2, 2, 2, 2] + }, emphasis: { itemStyle: { areaColor: "#8B9BD3", opacity: 1.0 + }, + label: { + show: true, + fontSize: "10px", + color: "#fff", + backgroundColor: "#8B9BD3", + padding: [2, 2, 2, 2] } }, //select: {itemStyle:{ areaColor: "#8B9BD3", opacity: 0.8 }}, @@ -107,6 +121,12 @@ Vue.component("traffic-map-box", { itemStyle: { areaColor: bgColor, opacity: fullOpacity + }, + label: { + show: true, + formatter: function (args) { + return args.name + } } }, label: { @@ -2254,6 +2274,7 @@ Vue.component("http-cache-refs-box", { - {{cacheRef.maxSize.count}}{{cacheRef.maxSize.unit}} 0 - {{cacheRef.maxSize.count}}{{cacheRef.maxSize.unit}} + {{cacheRef.methods.join(", ")}} 状态码:{{cacheRef.status.map(function(v) {return v.toString()}).join(", ")}} @@ -2515,13 +2536,16 @@ Vue.component("http-host-redirect-box", { // 单个缓存条件设置 Vue.component("http-cache-ref-box", { props: ["v-cache-ref", "v-is-reverse"], + mounted: function () { + this.$refs.variablesDescriber.update(this.ref.key) + }, data: function () { let ref = this.vCacheRef if (ref == null) { ref = { isOn: true, cachePolicyId: 0, - key: "${scheme}://${host}${requestURI}", + key: "${scheme}://${host}${requestPath}${isArgs}${args}", life: {count: 2, unit: "hour"}, status: [200], maxSize: {count: 32, unit: "mb"}, @@ -2531,9 +2555,17 @@ Vue.component("http-cache-ref-box", { enableRequestCachePragma: false, conds: null, allowChunkedEncoding: true, - isReverse: this.vIsReverse + isReverse: this.vIsReverse, + methods: [] } } + if (ref.key == null) { + ref.key = "" + } + if (ref.methods == null) { + ref.methods = [] + } + if (ref.life == null) { ref.life = {count: 2, unit: "hour"} } @@ -2574,6 +2606,14 @@ Vue.component("http-cache-ref-box", { result.push(statusNumber) }) this.ref.status = result + }, + changeMethods: function (methods) { + this.ref.methods = methods.map(function (v) { + return v.toUpperCase() + }) + }, + changeKey: function (key) { + this.$refs.variablesDescriber.update(key) } }, template: ` @@ -2594,13 +2634,20 @@ Vue.component("http-cache-ref-box", { 缓存Key * - -

用来区分不同缓存内容的唯一Key。

+ +

用来区分不同缓存内容的唯一Key。

+ + 请求方法 + + +

允许请求的缓存方法,默认支持所有的请求方法。

+ + 可缓存的最大内容尺寸 @@ -3791,6 +3838,7 @@ Vue.component("http-cache-refs-config-box", { - {{cacheRef.maxSize.count}}{{cacheRef.maxSize.unit}} 0 - {{cacheRef.maxSize.count}}{{cacheRef.maxSize.unit}} + {{cacheRef.methods.join(", ")}} 状态码:{{cacheRef.status.map(function(v) {return v.toString()}).join(", ")}} @@ -3808,7 +3856,7 @@ Vue.component("http-cache-refs-config-box", { -

所有条件匹配顺序为从上到下,可以拖动左侧的排序。

+

所有条件匹配顺序为从上到下,可以拖动左侧的排序。服务设置的优先级比全局缓存策略设置的优先级要高。

    +添加不缓存设置 @@ -5501,7 +5549,7 @@ Vue.component("http-header-policy-box", { responseDeletingHeaders = responsePolicy.deleteHeaders } } - + return { type: type, typeName: (type == "request") ? "请求" : "响应", @@ -5522,21 +5570,21 @@ Vue.component("http-header-policy-box", { addSettingHeader: function (policyId) { teaweb.popup("/servers/server/settings/headers/createSetPopup?" + this.vParams + "&headerPolicyId=" + policyId, { callback: function () { - window.location.reload() + teaweb.successRefresh("保存成功") } }) }, addDeletingHeader: function (policyId, type) { teaweb.popup("/servers/server/settings/headers/createDeletePopup?" + this.vParams + "&headerPolicyId=" + policyId + "&type=" + type, { callback: function () { - window.location.reload() + teaweb.successRefresh("保存成功") } }) }, updateSettingPopup: function (policyId, headerId) { teaweb.popup("/servers/server/settings/headers/updateSetPopup?" + this.vParams + "&headerPolicyId=" + policyId + "&headerId=" + headerId, { callback: function () { - window.location.reload() + teaweb.successRefresh("保存成功") } }) }, @@ -6393,7 +6441,8 @@ Vue.component("http-access-log-config-box", { }) return { - accessLog: accessLog + accessLog: accessLog, + hasRequestBodyField: this.vFields.$contains(8) } }, methods: { @@ -6403,6 +6452,7 @@ Vue.component("http-access-log-config-box", { }).map(function (v) { return v.code }) + this.hasRequestBodyField = this.accessLog.fields.$contains(8) } }, template: `
@@ -6423,12 +6473,19 @@ Vue.component("http-access-log-config-box", { - 要存储的访问日志字段 + 基础信息 +

默认记录客户端IP、请求URL等基础信息。

+ + + 高级信息 -
- - +
+ +
+

在基础信息之外要存储的信息。 + 记录"请求Body"将会显著消耗更多的系统资源,建议仅在调试时启用,最大记录尺寸为2MB。 +

@@ -10422,6 +10479,41 @@ Vue.component("health-check-config-box", {
` }) +// 将变量转换为中文 +Vue.component("request-variables-describer", { + data: function () { + return { + vars:[] + } + }, + methods: { + update: function (variablesString) { + this.vars = [] + let that = this + variablesString.replace(/\${.+?}/g, function (v) { + let def = that.findVar(v) + if (def == null) { + return v + } + that.vars.push(def) + }) + }, + findVar: function (name) { + let def = null + window.REQUEST_VARIABLES.forEach(function (v) { + if (v.code == name) { + def = v + } + }) + return def + } + }, + template: ` + {{v.code}} - {{v.name}} +` +}) + + Vue.component("time-duration-box", { props: ["v-name", "v-value", "v-count", "v-unit"], mounted: function () { @@ -10817,7 +10909,7 @@ Vue.component("node-role-name", { let sourceCodeBoxIndex = 0 Vue.component("source-code-box", { - props: ["name", "type", "id", "read-only"], + props: ["name", "type", "id", "read-only", "width", "height"], mounted: function () { let readOnly = this.readOnly if (typeof readOnly != "boolean") { @@ -10831,27 +10923,46 @@ Vue.component("source-code-box", { } else if (valueBox.innerText != null) { value = valueBox.innerText } - let boxEditor = CodeMirror.fromTextArea(box, { - theme: "idea", - lineNumbers: true, - value: "", - readOnly: readOnly, - showCursorWhenSelecting: true, - height: "auto", - //scrollbarStyle: null, - viewportMargin: Infinity, - lineWrapping: true, - highlightFormatting: false, - indentUnit: 4, - indentWithTabs: true - }) - boxEditor.setValue(value) - let info = CodeMirror.findModeByMIME(this.type) - if (info != null) { - boxEditor.setOption("mode", info.mode) - CodeMirror.modeURL = "/codemirror/mode/%N/%N.js" - CodeMirror.autoLoadMode(boxEditor, info.mode) + this.createEditor(box, value, readOnly) + }, + methods: { + createEditor: function (box, value, readOnly) { + let boxEditor = CodeMirror.fromTextArea(box, { + theme: "idea", + lineNumbers: true, + value: "", + readOnly: readOnly, + showCursorWhenSelecting: true, + height: "auto", + //scrollbarStyle: null, + viewportMargin: Infinity, + lineWrapping: true, + highlightFormatting: false, + indentUnit: 4, + indentWithTabs: true + }) + boxEditor.setValue(value) + + let width = this.width + let height = this.height + if (width != null && height != null) { + width = parseInt(width) + height = parseInt(height) + if (!isNaN(width) && !isNaN(height)) { + if (width <= 0) { + width = box.parentNode.offsetWidth + } + boxEditor.setSize(width, height) + } + } + + let info = CodeMirror.findModeByMIME(this.type) + if (info != null) { + boxEditor.setOption("mode", info.mode) + CodeMirror.modeURL = "/codemirror/mode/%N/%N.js" + CodeMirror.autoLoadMode(boxEditor, info.mode) + } } }, data: function () { @@ -12357,7 +12468,7 @@ window.REQUEST_COND_COMPONENTS = [{"type":"url-extension","name":"URL扩展名", window.REQUEST_COND_OPERATORS = [{"description":"判断是否正则表达式匹配","name":"正则表达式匹配","op":"regexp"},{"description":"判断是否正则表达式不匹配","name":"正则表达式不匹配","op":"not regexp"},{"description":"使用字符串对比参数值是否相等于某个值","name":"字符串等于","op":"eq"},{"description":"参数值包含某个前缀","name":"字符串前缀","op":"prefix"},{"description":"参数值包含某个后缀","name":"字符串后缀","op":"suffix"},{"description":"参数值包含另外一个字符串","name":"字符串包含","op":"contains"},{"description":"参数值不包含另外一个字符串","name":"字符串不包含","op":"not contains"},{"description":"使用字符串对比参数值是否不相等于某个值","name":"字符串不等于","op":"not"},{"description":"判断参数值在某个列表中","name":"在列表中","op":"in"},{"description":"判断参数值不在某个列表中","name":"不在列表中","op":"not in"},{"description":"判断小写的扩展名(不带点)在某个列表中","name":"扩展名","op":"file ext"},{"description":"判断MimeType在某个列表中,支持类似于image/*的语法","name":"MimeType","op":"mime type"},{"description":"判断版本号在某个范围内,格式为version1,version2","name":"版本号范围","op":"version range"},{"description":"将参数转换为整数数字后进行对比","name":"整数等于","op":"eq int"},{"description":"将参数转换为可以有小数的浮点数字进行对比","name":"浮点数等于","op":"eq float"},{"description":"将参数转换为数字进行对比","name":"数字大于","op":"gt"},{"description":"将参数转换为数字进行对比","name":"数字大于等于","op":"gte"},{"description":"将参数转换为数字进行对比","name":"数字小于","op":"lt"},{"description":"将参数转换为数字进行对比","name":"数字小于等于","op":"lte"},{"description":"对整数参数值取模,除数为10,对比值为余数","name":"整数取模10","op":"mod 10"},{"description":"对整数参数值取模,除数为100,对比值为余数","name":"整数取模100","op":"mod 100"},{"description":"对整数参数值取模,对比值格式为:除数,余数,比如10,1","name":"整数取模","op":"mod"},{"description":"将参数转换为IP进行对比","name":"IP等于","op":"eq ip"},{"description":"将参数转换为IP进行对比","name":"IP大于","op":"gt ip"},{"description":"将参数转换为IP进行对比","name":"IP大于等于","op":"gte ip"},{"description":"将参数转换为IP进行对比","name":"IP小于","op":"lt ip"},{"description":"将参数转换为IP进行对比","name":"IP小于等于","op":"lte ip"},{"description":"IP在某个范围之内,范围格式可以是英文逗号分隔的ip1,ip2,或者CIDR格式的ip/bits","name":"IP范围","op":"ip range"},{"description":"对IP参数值取模,除数为10,对比值为余数","name":"IP取模10","op":"ip mod 10"},{"description":"对IP参数值取模,除数为100,对比值为余数","name":"IP取模100","op":"ip mod 100"},{"description":"对IP参数值取模,对比值格式为:除数,余数,比如10,1","name":"IP取模","op":"ip mod"},{"description":"判断参数值解析后的文件是否存在","name":"文件存在","op":"file exist"},{"description":"判断参数值解析后的文件是否不存在","name":"文件不存在","op":"file not exist"}] -window.REQUEST_VARIABLES = [{"code":"${edgeVersion}","description":"","name":"边缘节点版本"},{"code":"${remoteAddr}","description":"会依次根据X-Forwarded-For、X-Real-IP、RemoteAddr获取,适合前端有别的反向代理服务时使用,存在伪造的风险","name":"客户端地址(IP)"},{"code":"${rawRemoteAddr}","description":"返回直接连接服务的客户端原始IP地址","name":"客户端地址(IP)"},{"code":"${remotePort}","description":"","name":"客户端端口"},{"code":"${remoteUser}","description":"","name":"客户端用户名"},{"code":"${requestURI}","description":"比如/hello?name=lily","name":"请求URI"},{"code":"${requestPath}","description":"比如/hello","name":"请求路径(不包括参数)"},{"code":"${requestURL}","description":"比如https://example.com/hello?name=lily","name":"完整的请求URL"},{"code":"${requestLength}","description":"","name":"请求内容长度"},{"code":"${requestMethod}","description":"比如GET、POST","name":"请求方法"},{"code":"${requestFilename}","description":"","name":"请求文件路径"},{"code":"${scheme}","description":"","name":"请求协议,http或https"},{"code":"${proto}","description:":"类似于HTTP/1.0","name":"包含版本的HTTP请求协议"},{"code":"${timeISO8601}","description":"比如2018-07-16T23:52:24.839+08:00","name":"ISO 8601格式的时间"},{"code":"${timeLocal}","description":"比如17/Jul/2018:09:52:24 +0800","name":"本地时间"},{"code":"${msec}","description":"比如1531756823.054","name":"带有毫秒的时间"},{"code":"${timestamp}","description":"","name":"unix时间戳,单位为秒"},{"code":"${host}","description":"","name":"主机名"},{"code":"${serverName}","description":"","name":"接收请求的服务器名"},{"code":"${serverPort}","description":"","name":"接收请求的服务器端口"},{"code":"${referer}","description":"","name":"请求来源URL"},{"code":"${referer.host}","description":"","name":"请求来源URL域名"},{"code":"${userAgent}","description":"","name":"客户端信息"},{"code":"${contentType}","description":"","name":"请求头部的Content-Type"},{"code":"${cookies}","description":"","name":"所有cookie组合字符串"},{"code":"${cookie.NAME}","description":"","name":"单个cookie值"},{"code":"${args}","description":"","name":"所有参数组合字符串"},{"code":"${arg.NAME}","description":"","name":"单个参数值"},{"code":"${headers}","description":"","name":"所有Header信息组合字符串"},{"code":"${header.NAME}","description":"","name":"单个Header值"}] +window.REQUEST_VARIABLES = [{"code":"${edgeVersion}","description":"","name":"边缘节点版本"},{"code":"${remoteAddr}","description":"会依次根据X-Forwarded-For、X-Real-IP、RemoteAddr获取,适合前端有别的反向代理服务时使用,存在伪造的风险","name":"客户端地址(IP)"},{"code":"${rawRemoteAddr}","description":"返回直接连接服务的客户端原始IP地址","name":"客户端地址(IP)"},{"code":"${remotePort}","description":"","name":"客户端端口"},{"code":"${remoteUser}","description":"","name":"客户端用户名"},{"code":"${requestURI}","description":"比如/hello?name=lily","name":"请求URI"},{"code":"${requestPath}","description":"比如/hello","name":"请求路径(不包括参数)"},{"code":"${requestURL}","description":"比如https://example.com/hello?name=lily","name":"完整的请求URL"},{"code":"${requestLength}","description":"","name":"请求内容长度"},{"code":"${requestMethod}","description":"比如GET、POST","name":"请求方法"},{"code":"${requestFilename}","description":"","name":"请求文件路径"},{"code":"${scheme}","description":"","name":"请求协议,http或https"},{"code":"${proto}","description:":"类似于HTTP/1.0","name":"包含版本的HTTP请求协议"},{"code":"${timeISO8601}","description":"比如2018-07-16T23:52:24.839+08:00","name":"ISO 8601格式的时间"},{"code":"${timeLocal}","description":"比如17/Jul/2018:09:52:24 +0800","name":"本地时间"},{"code":"${msec}","description":"比如1531756823.054","name":"带有毫秒的时间"},{"code":"${timestamp}","description":"","name":"unix时间戳,单位为秒"},{"code":"${host}","description":"","name":"主机名"},{"code":"${serverName}","description":"","name":"接收请求的服务器名"},{"code":"${serverPort}","description":"","name":"接收请求的服务器端口"},{"code":"${referer}","description":"","name":"请求来源URL"},{"code":"${referer.host}","description":"","name":"请求来源URL域名"},{"code":"${userAgent}","description":"","name":"客户端信息"},{"code":"${contentType}","description":"","name":"请求头部的Content-Type"},{"code":"${cookies}","description":"","name":"所有cookie组合字符串"},{"code":"${cookie.NAME}","description":"","name":"单个cookie值"},{"code":"${isArgs}","description":"如果URL有参数,则值为`?`;否则,则值为空","name":"问号(?)标记"},{"code":"${args}","description":"","name":"所有参数组合字符串"},{"code":"${arg.NAME}","description":"","name":"单个参数值"},{"code":"${headers}","description":"","name":"所有Header信息组合字符串"},{"code":"${header.NAME}","description":"","name":"单个Header值"}] window.METRIC_HTTP_KEYS = [{"name":"客户端地址(IP)","code":"${remoteAddr}","description":"会依次根据X-Forwarded-For、X-Real-IP、RemoteAddr获取,适用于前端可能有别的反向代理的情形,存在被伪造的可能","icon":""},{"name":"直接客户端地址(IP)","code":"${rawRemoteAddr}","description":"返回直接连接服务的客户端原始IP地址","icon":""},{"name":"客户端用户名","code":"${remoteUser}","description":"通过基本认证填入的用户名","icon":""},{"name":"请求URI","code":"${requestURI}","description":"包含参数,比如/hello?name=lily","icon":""},{"name":"请求路径","code":"${requestPath}","description":"不包含参数,比如/hello","icon":""},{"name":"完整URL","code":"${requestURL}","description":"比如https://example.com/hello?name=lily","icon":""},{"name":"请求方法","code":"${requestMethod}","description":"比如GET、POST等","icon":""},{"name":"请求协议Scheme","code":"${scheme}","description":"http或https","icon":""},{"name":"文件扩展名","code":"${requestPathExtension}","description":"请求路径中的文件扩展名,包括点符号,比如.html、.png","icon":""},{"name":"主机名","code":"${host}","description":"通常是请求的域名","icon":""},{"name":"请求协议Proto","code":"${proto}","description":"包含版本的HTTP请求协议,类似于HTTP/1.0","icon":""},{"name":"HTTP协议","code":"${proto}","description":"包含版本的HTTP请求协议,类似于HTTP/1.0","icon":""},{"name":"URL参数值","code":"${arg.NAME}","description":"单个URL参数值","icon":""},{"name":"请求来源URL","code":"${referer}","description":"请求来源Referer URL","icon":""},{"name":"请求来源URL域名","code":"${referer.host}","description":"请求来源Referer URL域名","icon":""},{"name":"Header值","code":"${header.NAME}","description":"单个Header值,比如${header.User-Agent}","icon":""},{"name":"Cookie值","code":"${cookie.NAME}","description":"单个cookie值,比如${cookie.sid}","icon":""},{"name":"状态码","code":"${status}","description":"","icon":""},{"name":"响应的Content-Type值","code":"${response.contentType}","description":"","icon":""}]