Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39f9fa0e6b | ||
|
|
0c8ae8960d | ||
|
|
ffd6a20829 | ||
|
|
2ee3e2782c | ||
|
|
61324c28c6 | ||
|
|
e9f70ecb90 | ||
|
|
cc3b802575 | ||
|
|
9b780ff4a3 | ||
|
|
deb1a33e78 | ||
|
|
5021350aa0 | ||
|
|
41b3dab135 | ||
|
|
ecf94170c8 | ||
|
|
56acdf3ce3 | ||
|
|
5d6d970d49 | ||
|
|
b501cf3c88 | ||
|
|
deac2baa18 | ||
|
|
b1e3f54055 | ||
|
|
8ebf79ffbe | ||
|
|
8d7307cddf | ||
|
|
c547837ae3 | ||
|
|
b408996e72 | ||
|
|
6b5866524d | ||
|
|
4994dfb488 | ||
|
|
5d5040651e | ||
|
|
5252188a68 | ||
|
|
3f664882d5 | ||
|
|
15f2a2d517 | ||
|
|
6637b0fb8f | ||
|
|
a06eeb9129 | ||
|
|
a24fce2c22 | ||
|
|
ddbdb64fc4 | ||
|
|
38c6d545ec | ||
|
|
fe880abbb0 | ||
|
|
081ed76c39 | ||
|
|
613a00686f |
@@ -4,8 +4,12 @@ ENV TZ "Asia/Shanghai"
|
||||
ENV VERSION 1.2.8
|
||||
ENV ROOT_DIR /usr/local/goedge
|
||||
ENV TAR_FILE edge-admin-linux-amd64-plus-v${VERSION}.zip
|
||||
|
||||
# remote official repository
|
||||
ENV TAR_URL "https://dl.goedge.cn/edge/v${VERSION}/edge-admin-linux-amd64-plus-v${VERSION}.zip"
|
||||
#ENV TAR_URL "http://192.168.2.61:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip" # your local repository
|
||||
|
||||
# your local repository
|
||||
#ENV TAR_URL "http://192.168.2.61:8080/edge-admin-linux-amd64-plus-v${VERSION}.zip"
|
||||
|
||||
RUN apk add --no-cache tzdata
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ func LoadAPIConfig() (*APIConfig, error) {
|
||||
|
||||
// 自动生成新配置文件
|
||||
if isFromOld {
|
||||
config.OldRPC.Endpoints = nil
|
||||
_ = config.WriteFile(Tea.ConfigFile(ConfigFileName))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "1.2.8"
|
||||
Version = "1.2.9"
|
||||
|
||||
APINodeVersion = "1.2.8"
|
||||
APINodeVersion = "1.2.9"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
@@ -35,6 +36,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
clusters = append(clusters, node.SecondaryNodeClusters...)
|
||||
var allDNSRouteMaps = map[int64][]maps.Map{} // domain id => routes
|
||||
var routeMaps = map[int64][]maps.Map{} // domain id => routes
|
||||
var domainIds = []int64{}
|
||||
for _, cluster := range clusters {
|
||||
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{
|
||||
NodeId: params.NodeId,
|
||||
@@ -49,6 +51,13 @@ func (this *IndexAction) RunGet(params struct {
|
||||
continue
|
||||
}
|
||||
var domainId = dnsInfo.DnsDomainId
|
||||
|
||||
// remove same domain
|
||||
if lists.ContainsInt64(domainIds, domainId) {
|
||||
continue
|
||||
}
|
||||
domainIds = append(domainIds, domainId)
|
||||
|
||||
var domainName = dnsInfo.DnsDomainName
|
||||
if len(dnsInfo.Routes) > 0 {
|
||||
for _, route := range dnsInfo.Routes {
|
||||
@@ -102,15 +111,23 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.NodeDNS_LogUpdateNodeDNS, params.NodeId)
|
||||
|
||||
dnsRouteCodes := []string{}
|
||||
var rawRouteCodes = []string{}
|
||||
if len(params.DnsRoutesJSON) > 0 {
|
||||
err := json.Unmarshal(params.DnsRoutesJSON, &dnsRouteCodes)
|
||||
err := json.Unmarshal(params.DnsRoutesJSON, &rawRouteCodes)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// remove duplications
|
||||
var dnsRouteCodes = []string{}
|
||||
for _, routeCode := range rawRouteCodes {
|
||||
if !lists.ContainsString(dnsRouteCodes, routeCode) {
|
||||
dnsRouteCodes = append(dnsRouteCodes, routeCode)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := this.RPC().NodeRPC().UpdateNodeDNS(this.AdminContext(), &pb.UpdateNodeDNSRequest{
|
||||
NodeId: params.NodeId,
|
||||
IpAddr: "",
|
||||
|
||||
@@ -43,13 +43,19 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["config"] = config
|
||||
|
||||
var httpAllDomainMismatchActionCode = serverconfigs.DomainMismatchActionPage
|
||||
var httpAllDomainMismatchActionContentHTML string
|
||||
var httpAllDomainMismatchActionStatusCode = "404"
|
||||
if config.HTTPAll.DomainMismatchAction != nil && config.HTTPAll.DomainMismatchAction.Options != nil {
|
||||
httpAllDomainMismatchActionContentHTML = config.HTTPAll.DomainMismatchAction.Options.GetString("contentHTML")
|
||||
var statusCode = config.HTTPAll.DomainMismatchAction.Options.GetInt("statusCode")
|
||||
if statusCode > 0 {
|
||||
httpAllDomainMismatchActionStatusCode = types.String(statusCode)
|
||||
if config.HTTPAll.DomainMismatchAction != nil {
|
||||
httpAllDomainMismatchActionCode = config.HTTPAll.DomainMismatchAction.Code
|
||||
|
||||
if config.HTTPAll.DomainMismatchAction.Options != nil {
|
||||
// 即使是非 page 处理动作,也读取这些内容,以便于在切换到 page 时,可以顺利读取到先前的设置
|
||||
httpAllDomainMismatchActionContentHTML = config.HTTPAll.DomainMismatchAction.Options.GetString("contentHTML")
|
||||
var statusCode = config.HTTPAll.DomainMismatchAction.Options.GetInt("statusCode")
|
||||
if statusCode > 0 {
|
||||
httpAllDomainMismatchActionStatusCode = types.String(statusCode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
httpAllDomainMismatchActionContentHTML = `<!DOCTYPE html>
|
||||
@@ -73,6 +79,7 @@ p { color: grey; }
|
||||
</html>`
|
||||
}
|
||||
|
||||
this.Data["httpAllDomainMismatchActionCode"] = httpAllDomainMismatchActionCode
|
||||
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
|
||||
this.Data["httpAllDomainMismatchActionStatusCode"] = httpAllDomainMismatchActionStatusCode
|
||||
|
||||
@@ -83,6 +90,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
|
||||
HttpAllMatchDomainStrictly bool
|
||||
HttpAllDomainMismatchActionCode string
|
||||
HttpAllDomainMismatchActionContentHTML string
|
||||
HttpAllDomainMismatchActionStatusCode string
|
||||
HttpAllAllowMismatchDomainsJSON []byte
|
||||
@@ -90,11 +98,13 @@ func (this *IndexAction) RunPost(params struct {
|
||||
HttpAllDefaultDomain string
|
||||
HttpAllNodeIPPageHTML string
|
||||
HttpAllNodeIPShowPage bool
|
||||
HttpAllEnableServerAddrVariable bool
|
||||
|
||||
HttpAllServerName string
|
||||
HttpAllSupportsLowVersionHTTP bool
|
||||
HttpAllMatchCertFromAllServers bool
|
||||
HttpAllForceLnRequest bool
|
||||
HttpAllServerName string
|
||||
HttpAllSupportsLowVersionHTTP bool
|
||||
HttpAllMatchCertFromAllServers bool
|
||||
HttpAllForceLnRequest bool
|
||||
HttpAllLnRequestSchedulingMethod string
|
||||
|
||||
HttpAccessLogIsOn bool
|
||||
HttpAccessLogEnableRequestHeaders bool
|
||||
@@ -139,7 +149,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
config.HTTPAll.MatchDomainStrictly = params.HttpAllMatchDomainStrictly
|
||||
config.HTTPAll.DomainMismatchAction = &serverconfigs.DomainMismatchAction{
|
||||
Code: serverconfigs.DomainMismatchActionPage,
|
||||
Code: params.HttpAllDomainMismatchActionCode,
|
||||
Options: maps.Map{
|
||||
"statusCode": domainMisMatchStatusCode,
|
||||
"contentHTML": params.HttpAllDomainMismatchActionContentHTML,
|
||||
@@ -167,6 +177,8 @@ func (this *IndexAction) RunPost(params struct {
|
||||
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
|
||||
config.HTTPAll.MatchCertFromAllServers = params.HttpAllMatchCertFromAllServers
|
||||
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
|
||||
config.HTTPAll.LnRequestSchedulingMethod = params.HttpAllLnRequestSchedulingMethod
|
||||
config.HTTPAll.EnableServerAddrVariable = params.HttpAllEnableServerAddrVariable
|
||||
|
||||
// 访问日志
|
||||
config.HTTPAccessLog.IsOn = params.HttpAccessLogIsOn
|
||||
|
||||
@@ -3,11 +3,11 @@ package log
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"github.com/tealeg/xlsx/v3"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ExportExcelAction struct {
|
||||
@@ -65,23 +65,11 @@ func (this *ExportExcelAction) RunGet(params struct {
|
||||
for _, log := range logsResp.Logs {
|
||||
var regionName = ""
|
||||
var ispName = ""
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: log.Ip})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if regionResp.IpRegion != nil {
|
||||
regionName = regionResp.IpRegion.Summary
|
||||
|
||||
// remove isp from regionName
|
||||
var index = strings.LastIndex(regionName, "|")
|
||||
if index > 0 {
|
||||
regionName = regionName[:index]
|
||||
}
|
||||
|
||||
if len(regionResp.IpRegion.Isp) > 0 {
|
||||
ispName = regionResp.IpRegion.Isp
|
||||
}
|
||||
var ipRegion = iplibrary.LookupIP(log.Ip)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
regionName = ipRegion.RegionSummary()
|
||||
ispName = ipRegion.ProviderName()
|
||||
}
|
||||
|
||||
var row = sheet.AddRow()
|
||||
|
||||
@@ -3,6 +3,7 @@ package log
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -84,14 +85,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
var logMaps = []maps.Map{}
|
||||
for _, log := range logsResp.Logs {
|
||||
regionName := ""
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: log.Ip})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if regionResp.IpRegion != nil {
|
||||
regionName = regionResp.IpRegion.Summary
|
||||
var regionName = ""
|
||||
var ipRegion = iplibrary.LookupIP(log.Ip)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
regionName = ipRegion.Summary()
|
||||
}
|
||||
|
||||
logMaps = append(logMaps, maps.Map{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -43,6 +44,14 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 去除端口
|
||||
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
|
||||
host, _, err := net.SplitHostPort(serverName)
|
||||
if err == nil && len(host) > 0 {
|
||||
serverName = host
|
||||
}
|
||||
}
|
||||
|
||||
params.Must.
|
||||
Field("serverName", serverName).
|
||||
Require("请输入域名")
|
||||
@@ -72,6 +81,14 @@ func (this *AddServerNamePopupAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 去除端口
|
||||
if regexp.MustCompile(`:\d+$`).MatchString(serverName) {
|
||||
host, _, err := net.SplitHostPort(serverName)
|
||||
if err == nil && len(host) > 0 {
|
||||
serverName = host
|
||||
}
|
||||
}
|
||||
|
||||
// 转成小写
|
||||
serverName = strings.ToLower(serverName)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package waf
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
@@ -141,20 +142,7 @@ func (this *LogAction) RunGet(params struct {
|
||||
this.Data["groups"] = groupMaps
|
||||
|
||||
// 根据IP查询区域
|
||||
regionMap := map[string]string{} // ip => region
|
||||
if len(ipList) > 0 {
|
||||
resp, err := this.RPC().IPLibraryRPC().LookupIPRegions(this.AdminContext(), &pb.LookupIPRegionsRequest{IpList: ipList})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.IpRegionMap != nil {
|
||||
for ip, region := range resp.IpRegionMap {
|
||||
regionMap[ip] = region.Summary
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMap
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
// WAF相关
|
||||
var wafInfos = map[int64]maps.Map{} // set id => WAF Map
|
||||
|
||||
@@ -564,9 +564,11 @@ func (this *CreateAction) RunPost(params struct {
|
||||
var remoteAddrConfig = &serverconfigs.HTTPRemoteAddrConfig{
|
||||
IsOn: true,
|
||||
Value: "${rawRemoteAddr}",
|
||||
Type: serverconfigs.HTTPRemoteAddrTypeDefault,
|
||||
}
|
||||
if params.RemoteAddrIsOn {
|
||||
remoteAddrConfig.Value = "${remoteAddr}"
|
||||
remoteAddrConfig.Type = serverconfigs.HTTPRemoteAddrTypeProxy
|
||||
}
|
||||
remoteAddrConfigJSON, err := json.Marshal(remoteAddrConfig)
|
||||
if err != nil {
|
||||
|
||||
@@ -42,7 +42,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyRef"] = reverseProxyRef
|
||||
|
||||
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -34,7 +34,7 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
reverseProxy := serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -36,14 +36,14 @@ func (this *SettingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
|
||||
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyRefJSON, reverseProxyRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -67,7 +67,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -110,6 +110,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
AddHeaders: reverseProxyConfig.AddHeaders,
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -54,9 +55,29 @@ func (this *IndexAction) RunPost(params struct {
|
||||
err := json.Unmarshal(params.RemoteAddrJSON, remoteAddrConfig)
|
||||
if err != nil {
|
||||
this.Fail("参数校验失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
remoteAddrConfig.Value = strings.TrimSpace(remoteAddrConfig.Value)
|
||||
|
||||
switch remoteAddrConfig.Type {
|
||||
case serverconfigs.HTTPRemoteAddrTypeRequestHeader:
|
||||
if len(remoteAddrConfig.RequestHeaderName) == 0 {
|
||||
this.FailField("requestHeaderName", "请输入请求报头")
|
||||
return
|
||||
}
|
||||
if !regexp.MustCompile(`^[\w-_,]+$`).MatchString(remoteAddrConfig.RequestHeaderName) {
|
||||
this.FailField("requestHeaderName", "请求报头中只能含有数字、英文字母、下划线、中划线")
|
||||
return
|
||||
}
|
||||
remoteAddrConfig.Value = "${header." + remoteAddrConfig.RequestHeaderName + "}"
|
||||
case serverconfigs.HTTPRemoteAddrTypeVariable:
|
||||
if len(remoteAddrConfig.Value) == 0 {
|
||||
this.FailField("value", "请输入自定义变量")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = remoteAddrConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
|
||||
@@ -42,7 +42,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyRef"] = reverseProxyRef
|
||||
|
||||
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -34,7 +34,7 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -42,8 +42,8 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyId"] = reverseProxy.Id
|
||||
|
||||
schedulingCode := reverseProxy.FindSchedulingConfig().Code
|
||||
schedulingMap := schedulingconfigs.FindSchedulingType(schedulingCode)
|
||||
var schedulingCode = reverseProxy.FindSchedulingConfig().Code
|
||||
var schedulingMap = schedulingconfigs.FindSchedulingType(schedulingCode)
|
||||
if schedulingMap == nil {
|
||||
this.ErrorPage(errors.New("invalid scheduling code '" + schedulingCode + "'"))
|
||||
return
|
||||
|
||||
@@ -36,14 +36,14 @@ func (this *SettingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
|
||||
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyRefJSON, reverseProxyRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -67,7 +67,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -110,6 +110,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
AddHeaders: reverseProxyConfig.AddHeaders,
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -42,7 +42,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyRef"] = reverseProxyRef
|
||||
|
||||
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -34,7 +34,7 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -43,7 +43,7 @@ func (this *SettingAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -67,7 +67,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -4,10 +4,10 @@ package ipbox
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -25,24 +25,15 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["ip"] = params.Ip
|
||||
|
||||
// IP信息
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: params.Ip})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
this.Data["regions"] = ""
|
||||
this.Data["isp"] = ""
|
||||
if regionResp.IpRegion != nil {
|
||||
var regionName = regionResp.IpRegion.Summary
|
||||
|
||||
// remove isp from regionName
|
||||
var index = strings.LastIndex(regionName, "|")
|
||||
if index > 0 {
|
||||
regionName = regionName[:index]
|
||||
}
|
||||
this.Data["regions"] = regionName
|
||||
this.Data["isp"] = regionResp.IpRegion.Isp
|
||||
var ipRegion = iplibrary.LookupIP(params.Ip)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
this.Data["regions"] = ipRegion.RegionSummary()
|
||||
this.Data["isp"] = ipRegion.ProviderName()
|
||||
}
|
||||
|
||||
// IP列表
|
||||
|
||||
@@ -4,11 +4,11 @@ package iplists
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -162,22 +162,10 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var region = ""
|
||||
var isp = ""
|
||||
if len(item.IpFrom) > 0 && len(item.IpTo) == 0 {
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: item.IpFrom})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var ipRegion = regionResp.IpRegion
|
||||
if ipRegion != nil {
|
||||
region = ipRegion.Summary
|
||||
|
||||
// remove isp from regionName
|
||||
var index = strings.LastIndex(region, "|")
|
||||
if index > 0 {
|
||||
region = region[:index]
|
||||
}
|
||||
|
||||
isp = ipRegion.Isp
|
||||
var ipRegion = iplibrary.LookupIP(item.IpFrom)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
region = ipRegion.RegionSummary()
|
||||
isp = ipRegion.ProviderName()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ package iplists
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -109,22 +109,10 @@ func (this *ItemsAction) RunGet(params struct {
|
||||
var region = ""
|
||||
var isp = ""
|
||||
if len(item.IpFrom) > 0 && len(item.IpTo) == 0 {
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: item.IpFrom})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var ipRegion = regionResp.IpRegion
|
||||
if ipRegion != nil {
|
||||
region = ipRegion.Summary
|
||||
|
||||
// remove isp from regionName
|
||||
var index = strings.LastIndex(region, "|")
|
||||
if index > 0 {
|
||||
region = region[:index]
|
||||
}
|
||||
|
||||
isp = ipRegion.Isp
|
||||
var ipRegion = iplibrary.LookupIP(item.IpFrom)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
region = ipRegion.RegionSummary()
|
||||
isp = ipRegion.ProviderName()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package logs
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -169,20 +170,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// 根据IP查询区域
|
||||
var regionMap = map[string]string{} // ip => region
|
||||
if len(ipList) > 0 {
|
||||
resp, err := this.RPC().IPLibraryRPC().LookupIPRegions(this.AdminContext(), &pb.LookupIPRegionsRequest{IpList: ipList})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.IpRegionMap != nil {
|
||||
for ip, region := range resp.IpRegionMap {
|
||||
regionMap[ip] = region.Summary
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMap
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
// WAF相关
|
||||
var wafInfos = map[int64]maps.Map{} // set id => WAF Map
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -160,20 +161,7 @@ func (this *HistoryAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// 根据IP查询区域
|
||||
var regionMap = map[string]string{} // ip => region
|
||||
if len(ipList) > 0 {
|
||||
resp, err := this.RPC().IPLibraryRPC().LookupIPRegions(this.AdminContext(), &pb.LookupIPRegionsRequest{IpList: ipList})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.IpRegionMap != nil {
|
||||
for ip, region := range resp.IpRegionMap {
|
||||
regionMap[ip] = region.Summary
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMap
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
// WAF相关
|
||||
var wafInfos = map[int64]maps.Map{} // set id => WAF Map
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
@@ -129,20 +130,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Data["hasMore"] = accessLogsResp.HasMore
|
||||
|
||||
// 根据IP查询区域
|
||||
var regionMap = map[string]string{} // ip => region
|
||||
if len(ipList) > 0 {
|
||||
resp, err := this.RPC().IPLibraryRPC().LookupIPRegions(this.AdminContext(), &pb.LookupIPRegionsRequest{IpList: ipList})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.IpRegionMap != nil {
|
||||
for ip, region := range resp.IpRegionMap {
|
||||
regionMap[ip] = region.Summary
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMap
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
// WAF相关
|
||||
var wafInfos = map[int64]maps.Map{} // set id => WAF Map
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -141,20 +142,7 @@ func (this *TodayAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
// 根据IP查询区域
|
||||
var regionMap = map[string]string{} // ip => region
|
||||
if len(ipList) > 0 {
|
||||
resp, err := this.RPC().IPLibraryRPC().LookupIPRegions(this.AdminContext(), &pb.LookupIPRegionsRequest{IpList: ipList})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.IpRegionMap != nil {
|
||||
for ip, region := range resp.IpRegionMap {
|
||||
regionMap[ip] = region.Summary
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Data["regions"] = regionMap
|
||||
this.Data["regions"] = iplibrary.LookupIPSummaries(ipList)
|
||||
|
||||
// WAF相关
|
||||
var wafInfos = map[int64]maps.Map{} // set id => WAF Map
|
||||
|
||||
@@ -2,6 +2,7 @@ package log
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net/http"
|
||||
@@ -86,24 +87,11 @@ func (this *ViewPopupAction) RunGet(params struct {
|
||||
|
||||
// 地域相关
|
||||
var regionMap maps.Map = nil
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: accessLog.RemoteAddr})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
region := regionResp.IpRegion
|
||||
if region != nil {
|
||||
var regionName = region.Summary
|
||||
|
||||
// remove isp from regionName
|
||||
var index = strings.LastIndex(regionName, "|")
|
||||
if index > 0 {
|
||||
regionName = regionName[:index]
|
||||
}
|
||||
|
||||
var ipRegion = iplibrary.LookupIP(accessLog.RemoteAddr)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
regionMap = maps.Map{
|
||||
"full": regionName,
|
||||
"isp": region.Isp,
|
||||
"full": ipRegion.RegionSummary(),
|
||||
"isp": ipRegion.ProviderName(),
|
||||
}
|
||||
}
|
||||
this.Data["region"] = regionMap
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -46,9 +47,29 @@ func (this *IndexAction) RunPost(params struct {
|
||||
err := json.Unmarshal(params.RemoteAddrJSON, remoteAddrConfig)
|
||||
if err != nil {
|
||||
this.Fail("参数校验失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
remoteAddrConfig.Value = strings.TrimSpace(remoteAddrConfig.Value)
|
||||
|
||||
switch remoteAddrConfig.Type {
|
||||
case serverconfigs.HTTPRemoteAddrTypeRequestHeader:
|
||||
if len(remoteAddrConfig.RequestHeaderName) == 0 {
|
||||
this.FailField("requestHeaderName", "请输入请求报头")
|
||||
return
|
||||
}
|
||||
if !regexp.MustCompile(`^[\w-_,]+$`).MatchString(remoteAddrConfig.RequestHeaderName) {
|
||||
this.FailField("requestHeaderName", "请求报头中只能含有数字、英文字母、下划线、中划线")
|
||||
return
|
||||
}
|
||||
remoteAddrConfig.Value = "${header." + remoteAddrConfig.RequestHeaderName + "}"
|
||||
case serverconfigs.HTTPRemoteAddrTypeVariable:
|
||||
if len(remoteAddrConfig.Value) == 0 {
|
||||
this.FailField("value", "请输入自定义变量")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = remoteAddrConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
|
||||
@@ -41,7 +41,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyRef"] = reverseProxyRef
|
||||
|
||||
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -18,7 +18,7 @@ func (this *SchedulingAction) Init() {
|
||||
}
|
||||
|
||||
func (this *SchedulingAction) RunGet(params struct {
|
||||
ServerId int64
|
||||
ServerId int64
|
||||
LocationId int64
|
||||
}) {
|
||||
reverseProxyResp, err := this.RPC().HTTPLocationRPC().FindAndInitHTTPLocationReverseProxyConfig(this.AdminContext(), &pb.FindAndInitHTTPLocationReverseProxyConfigRequest{LocationId: params.LocationId})
|
||||
@@ -26,7 +26,7 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -34,8 +34,8 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyId"] = reverseProxy.Id
|
||||
|
||||
schedulingCode := reverseProxy.FindSchedulingConfig().Code
|
||||
schedulingMap := schedulingconfigs.FindSchedulingType(schedulingCode)
|
||||
var schedulingCode = reverseProxy.FindSchedulingConfig().Code
|
||||
var schedulingMap = schedulingconfigs.FindSchedulingType(schedulingCode)
|
||||
if schedulingMap == nil {
|
||||
this.ErrorPage(errors.New("invalid scheduling code '" + schedulingCode + "'"))
|
||||
return
|
||||
|
||||
@@ -34,7 +34,7 @@ func (this *SettingAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -58,7 +58,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
|
||||
// TODO 校验配置
|
||||
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -101,6 +101,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
AddHeaders: reverseProxyConfig.AddHeaders,
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -63,6 +64,25 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
remoteAddrConfig.Value = strings.TrimSpace(remoteAddrConfig.Value)
|
||||
|
||||
switch remoteAddrConfig.Type {
|
||||
case serverconfigs.HTTPRemoteAddrTypeRequestHeader:
|
||||
if len(remoteAddrConfig.RequestHeaderName) == 0 {
|
||||
this.FailField("requestHeaderName", "请输入请求报头")
|
||||
return
|
||||
}
|
||||
if !regexp.MustCompile(`^[\w-_,]+$`).MatchString(remoteAddrConfig.RequestHeaderName) {
|
||||
this.FailField("requestHeaderName", "请求报头中只能含有数字、英文字母、下划线、中划线")
|
||||
return
|
||||
}
|
||||
remoteAddrConfig.Value = "${header." + remoteAddrConfig.RequestHeaderName + "}"
|
||||
case serverconfigs.HTTPRemoteAddrTypeVariable:
|
||||
if len(remoteAddrConfig.Value) == 0 {
|
||||
this.FailField("value", "请输入自定义变量")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = remoteAddrConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("配置校验失败:" + err.Error())
|
||||
|
||||
@@ -68,7 +68,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
this.Data["reverseProxyRef"] = reverseProxyRef
|
||||
|
||||
var reverseProxy = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -25,7 +25,7 @@ func (this *SchedulingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -28,14 +28,14 @@ func (this *SettingAction) RunGet(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
reverseProxyRef := &serverconfigs.ReverseProxyRef{}
|
||||
var reverseProxyRef = &serverconfigs.ReverseProxyRef{}
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyRefJSON, reverseProxyRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(reverseProxyResp.ReverseProxyJSON, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -57,7 +57,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
}) {
|
||||
defer this.CreateLogInfo(codes.ServerReverseProxy_LogUpdateServerReverseProxySettings, params.ServerId)
|
||||
|
||||
var reverseProxyConfig = &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err := json.Unmarshal(params.ReverseProxyJSON, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -133,6 +133,7 @@ func (this *SettingAction) RunPost(params struct {
|
||||
ProxyProtocolJSON: proxyProtocolJSON,
|
||||
FollowRedirects: reverseProxyConfig.FollowRedirects,
|
||||
RequestHostExcludingPort: reverseProxyConfig.RequestHostExcludingPort,
|
||||
Retry50X: reverseProxyConfig.Retry50X,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -43,7 +43,7 @@ func (this *UpdateSchedulingPopupAction) RunGet(params struct {
|
||||
}
|
||||
configData := reverseProxyResp.ReverseProxyJSON
|
||||
|
||||
reverseProxyConfig := &serverconfigs.ReverseProxyConfig{}
|
||||
var reverseProxyConfig = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(configData, reverseProxyConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -126,8 +126,8 @@ func (this *UpdateSchedulingPopupAction) RunPost(params struct {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
configData := reverseProxyResp.ReverseProxyJSON
|
||||
reverseProxy := &serverconfigs.ReverseProxyConfig{}
|
||||
var configData = reverseProxyResp.ReverseProxyJSON
|
||||
var reverseProxy = serverconfigs.NewReverseProxyConfig()
|
||||
err = json.Unmarshal(configData, reverseProxy)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
|
||||
@@ -41,6 +41,7 @@ func (this *CleanSettingAction) RunPost(params struct {
|
||||
ServerAccessLogCleanDays int
|
||||
ServerBandwidthStatCleanDays int
|
||||
UserBandwidthStatCleanDays int
|
||||
UserPlanBandwidthStatCleanDays int
|
||||
ServerDailyStatCleanDays int
|
||||
ServerDomainHourlyStatCleanDays int
|
||||
TrafficDailyStatCleanDays int
|
||||
@@ -85,6 +86,11 @@ func (this *CleanSettingAction) RunPost(params struct {
|
||||
}
|
||||
config.UserBandwidthStat.Clean.Days = params.UserBandwidthStatCleanDays
|
||||
|
||||
if params.UserPlanBandwidthStatCleanDays < 0 {
|
||||
params.UserPlanBandwidthStatCleanDays = 0
|
||||
}
|
||||
config.UserPlanBandwidthStat.Clean.Days = params.UserPlanBandwidthStatCleanDays
|
||||
|
||||
if params.ServerDailyStatCleanDays < 0 {
|
||||
params.ServerDailyStatCleanDays = 0
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
|
||||
}
|
||||
|
||||
// waiting for startup
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := 0; i < 30; i++ {
|
||||
_, err = net.Dial("tcp", "127.0.0.1:3306")
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
@@ -362,7 +362,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
|
||||
this.log("changing mysql password ...")
|
||||
var passwordSQL = "ALTER USER 'root'@'localhost' IDENTIFIED BY '" + newPassword + "';"
|
||||
{
|
||||
var cmd = utils.NewCmd("sh", "-c", baseDir+"/bin/mysql --user=root --password=\""+generatedPassword+"\" --execute=\""+passwordSQL+"\" --connect-expired-password")
|
||||
var cmd = utils.NewCmd("sh", "-c", baseDir+"/bin/mysql --host=\"127.0.0.1\" --user=root --password=\""+generatedPassword+"\" --execute=\""+passwordSQL+"\" --connect-expired-password")
|
||||
cmd.WithStderr()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
@@ -60,13 +61,9 @@ func (this *UserAction) RunGet(params struct {
|
||||
// IP地址
|
||||
var registeredRegion = ""
|
||||
if len(user.RegisteredIP) > 0 {
|
||||
regionResp, err := this.RPC().IPLibraryRPC().LookupIPRegion(this.AdminContext(), &pb.LookupIPRegionRequest{Ip: user.RegisteredIP})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if regionResp.IpRegion != nil {
|
||||
registeredRegion = regionResp.IpRegion.Summary
|
||||
var ipRegion = iplibrary.LookupIP(user.RegisteredIP)
|
||||
if ipRegion != nil && ipRegion.IsOk() {
|
||||
registeredRegion = ipRegion.Summary()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ package helpers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/events"
|
||||
nodes "github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/regionconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
@@ -77,23 +77,25 @@ func checkIPWithoutCache(config *systemconfigs.SecurityConfig, ipAddr string) bo
|
||||
|
||||
// 检查位置
|
||||
if len(config.AllowCountryIds) > 0 || len(config.AllowProvinceIds) > 0 {
|
||||
rpc, err := nodes.SharedRPC()
|
||||
if err != nil {
|
||||
logs.Println("[USER_MUST_AUTH][ERROR]" + err.Error())
|
||||
var userRegion = iplibrary.Lookup(ip)
|
||||
if userRegion == nil || !userRegion.IsOk() {
|
||||
return false
|
||||
}
|
||||
resp, err := rpc.IPLibraryRPC().LookupIPRegion(rpc.Context(0), &pb.LookupIPRegionRequest{Ip: ipAddr})
|
||||
if err != nil {
|
||||
logs.Println("[USER_MUST_AUTH][ERROR]" + err.Error())
|
||||
return false
|
||||
if len(config.AllowCountryIds) > 0 {
|
||||
// 检查大中华区
|
||||
var found = false
|
||||
for _, countryId := range config.AllowCountryIds {
|
||||
if regionconfigs.MatchUserRegion(userRegion.CountryId(), userRegion.ProvinceId(), countryId) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if resp.IpRegion == nil {
|
||||
return true
|
||||
}
|
||||
if len(config.AllowCountryIds) > 0 && !lists.ContainsInt64(config.AllowCountryIds, resp.IpRegion.CountryId) {
|
||||
return false
|
||||
}
|
||||
if len(config.AllowProvinceIds) > 0 && !lists.ContainsInt64(config.AllowProvinceIds, resp.IpRegion.ProvinceId) {
|
||||
if len(config.AllowProvinceIds) > 0 && !lists.ContainsInt64(config.AllowProvinceIds, userRegion.ProvinceId()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2840,6 +2840,42 @@ Vue.component("plan-user-selector", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 显示流量限制说明
|
||||
Vue.component("plan-limit-view", {
|
||||
props: ["value", "v-single-mode"],
|
||||
data: function () {
|
||||
let config = this.value
|
||||
|
||||
let hasLimit = false
|
||||
if (!this.vSingleMode) {
|
||||
if (config.trafficLimit != null && config.trafficLimit.isOn && ((config.trafficLimit.dailySize != null && config.trafficLimit.dailySize.count > 0) || (config.trafficLimit.monthlySize != null && config.trafficLimit.monthlySize.count > 0))) {
|
||||
hasLimit = true
|
||||
} else if (config.dailyRequests > 0 || config.monthlyRequests > 0) {
|
||||
hasLimit = true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
config: config,
|
||||
hasLimit: hasLimit
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatNumber: function (n) {
|
||||
return teaweb.formatNumber(n)
|
||||
}
|
||||
},
|
||||
template: `<div style="font-size: 0.8em; color: grey">
|
||||
<div class="ui divider" v-if="hasLimit"></div>
|
||||
<div v-if="config.trafficLimit != null && config.trafficLimit.isOn">
|
||||
<span v-if="config.trafficLimit.dailySize != null && config.trafficLimit.dailySize.count > 0">日流量限制:{{config.trafficLimit.dailySize.count}}{{config.trafficLimit.dailySize.unit.toUpperCase()}}<br/></span>
|
||||
<span v-if="config.trafficLimit.monthlySize != null && config.trafficLimit.monthlySize.count > 0">月流量限制:{{config.trafficLimit.monthlySize.count}}{{config.trafficLimit.monthlySize.unit.toUpperCase()}}<br/></span>
|
||||
</div>
|
||||
<div v-if="config.dailyRequests > 0">单日请求数限制:{{formatNumber(config.dailyRequests)}}</div>
|
||||
<div v-if="config.monthlyRequests > 0">单月请求数限制:{{formatNumber(config.monthlyRequests)}}</div>
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("plan-price-view", {
|
||||
props: ["v-plan"],
|
||||
data: function () {
|
||||
@@ -3033,6 +3069,7 @@ Vue.component("plan-price-config-box", {
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="monthlyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -3042,6 +3079,7 @@ Vue.component("plan-price-config-box", {
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="seasonallyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -3051,6 +3089,7 @@ Vue.component("plan-price-config-box", {
|
||||
<input type="text" style="width: 7em" maxlength="10" v-model="yearlyPrice"/>
|
||||
<span class="ui label">元</span>
|
||||
</div>
|
||||
<p class="comment">如果为0表示免费。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -4505,44 +4544,6 @@ Vue.component("http-firewall-actions-view", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("http-request-scripts-config-box", {
|
||||
props: ["vRequestScriptsConfig", "v-is-location"],
|
||||
data: function () {
|
||||
let config = this.vRequestScriptsConfig
|
||||
if (config == null) {
|
||||
config = {}
|
||||
}
|
||||
return {
|
||||
config: config
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeInitGroup: function (group) {
|
||||
this.config.initGroup = group
|
||||
this.$forceUpdate()
|
||||
},
|
||||
changeRequestGroup: function (group) {
|
||||
this.config.requestGroup = group
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="requestScriptsJSON" :value="JSON.stringify(config)"/>
|
||||
<div class="margin"></div>
|
||||
<h4 style="margin-bottom: 0">请求初始化</h4>
|
||||
<p class="comment">在请求刚初始化时调用,此时自定义Header等尚未生效。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.initGroup" @change="changeInitGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<h4 style="margin-bottom: 0">准备发送请求</h4>
|
||||
<p class="comment">在准备执行请求或者转发请求之前调用,此时自定义Header、源站等已准备好。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.requestGroup" @change="changeRequestGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 显示WAF规则的标签
|
||||
Vue.component("http-firewall-rule-label", {
|
||||
props: ["v-rule"],
|
||||
@@ -5333,7 +5334,7 @@ Vue.component("http-cache-ref-box", {
|
||||
<input type="checkbox" value="1" v-model="ref.skipSetCookie"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,当响应的Header中有Set-Cookie时不缓存响应内容。</p>
|
||||
<p class="comment">选中后,当响应的报头中有Set-Cookie时不缓存响应内容,防止动态内容被缓存。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
@@ -5343,7 +5344,7 @@ Vue.component("http-cache-ref-box", {
|
||||
<input type="checkbox" name="enableRequestCachePragma" value="1" v-model="ref.enableRequestCachePragma"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,当请求的Header中含有Pragma: no-cache或Cache-Control: no-cache时,会跳过缓存直接读取源内容。</p>
|
||||
<p class="comment">选中后,当请求的报头中含有Pragma: no-cache或Cache-Control: no-cache时,会跳过缓存直接读取源内容,一般仅用于调试。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
@@ -5699,7 +5700,7 @@ Vue.component("http-firewall-config-box", {
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible"></more-options-tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible" v-show="firewall.isOn"></more-options-tbody>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>启用系统全局规则</td>
|
||||
@@ -8614,11 +8615,15 @@ Vue.component("http-firewall-actions-box", {
|
||||
}
|
||||
|
||||
var defaultPageBody = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<title>403 Forbidden</title>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
<body>
|
||||
<h1>403 Forbidden</h1>
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -9233,7 +9238,7 @@ Vue.component("http-firewall-actions-box", {
|
||||
<span v-if="config.code == 'post_307' && config.options.life > 0">:有效期{{config.options.life}}秒</span>
|
||||
|
||||
<!-- record_ip -->
|
||||
<span v-if="config.code == 'record_ip'">:{{config.options.ipListName}}</span>
|
||||
<span v-if="config.code == 'record_ip'">:<span :class="{red: config.options.ipListIsDeleted}">{{config.options.ipListName}}</span></span>
|
||||
|
||||
<!-- tag -->
|
||||
<span v-if="config.code == 'tag'">:{{config.options.tags.join(", ")}}</span>
|
||||
@@ -10178,17 +10183,21 @@ Vue.component("http-pages-and-shutdown-box", {
|
||||
},
|
||||
addShutdownHTMLTemplate: function () {
|
||||
this.shutdownConfig.body = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
\t<title>升级中</title>
|
||||
\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>网站升级中</h1>
|
||||
<p>为了给您提供更好的服务,我们正在升级网站,请稍后重新访问。</p>
|
||||
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
@@ -11560,23 +11569,6 @@ Vue.component("http-access-log-config-box", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 显示流量限制说明
|
||||
Vue.component("traffic-limit-view", {
|
||||
props: ["v-traffic-limit"],
|
||||
data: function () {
|
||||
return {
|
||||
config: this.vTrafficLimit
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<div v-if="config.isOn">
|
||||
<span v-if="config.dailySize != null && config.dailySize.count > 0">日流量限制:{{config.dailySize.count}}{{config.dailySize.unit.toUpperCase()}}<br/></span>
|
||||
<span v-if="config.monthlySize != null && config.monthlySize.count > 0">月流量限制:{{config.monthlySize.count}}{{config.monthlySize.unit.toUpperCase()}}<br/></span>
|
||||
</div>
|
||||
<span v-else class="disabled">没有限制。</span>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 基本认证用户配置
|
||||
Vue.component("http-auth-basic-auth-user-box", {
|
||||
props: ["v-users"],
|
||||
@@ -12041,7 +12033,8 @@ Vue.component("reverse-proxy-box", {
|
||||
idleTimeout: {count: 0, unit: "second"},
|
||||
maxConns: 0,
|
||||
maxIdleConns: 0,
|
||||
followRedirects: false
|
||||
followRedirects: false,
|
||||
retry50X: true
|
||||
}
|
||||
}
|
||||
if (reverseProxyConfig.addHeaders == null) {
|
||||
@@ -12181,6 +12174,7 @@ Vue.component("reverse-proxy-box", {
|
||||
<input type="checkbox" v-model="reverseProxyRef.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,所有源站设置才会生效。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
@@ -12192,7 +12186,7 @@ Vue.component("reverse-proxy-box", {
|
||||
<div v-show="reverseProxyConfig.requestHostType == 2" style="margin-top: 0.8em">
|
||||
<input type="text" placeholder="比如example.com" v-model="reverseProxyConfig.requestHost"/>
|
||||
</div>
|
||||
<p class="comment">请求源站时的Host,用于修改源站接收到的域名
|
||||
<p class="comment">请求源站时的主机名(Host),用于修改源站接收到的域名
|
||||
<span v-if="reverseProxyConfig.requestHostType == 0">,"跟随CDN域名"是指源站接收到的域名和当前CDN访问域名保持一致</span>
|
||||
<span v-if="reverseProxyConfig.requestHostType == 1">,"跟随源站"是指源站接收到的域名仍然是填写的源站地址中的信息,不随代理服务域名改变而改变</span>
|
||||
<span v-if="reverseProxyConfig.requestHostType == 2">,自定义Host内容中支持请求变量</span>。</p>
|
||||
@@ -12215,7 +12209,7 @@ Vue.component("reverse-proxy-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动添加的Header</td>
|
||||
<td>自动添加报头</td>
|
||||
<td>
|
||||
<div>
|
||||
<div style="width: 14em; float: left; margin-bottom: 1em" v-for="header in forwardHeaders" :key="header.name">
|
||||
@@ -12223,7 +12217,7 @@ Vue.component("reverse-proxy-box", {
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<p class="comment">选中后,会自动向源站请求添加这些Header。</p>
|
||||
<p class="comment">选中后,会自动向源站请求添加这些报头,以便于源站获取客户端信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
@@ -12314,6 +12308,13 @@ Vue.component("reverse-proxy-box", {
|
||||
<p class="comment">源站保持等待的空闲超时时间,0表示使用默认时间。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动重试50X</td>
|
||||
<td>
|
||||
<checkbox v-model="reverseProxyConfig.retry50X"></checkbox>
|
||||
<p class="comment">选中后,表示当源站返回状态码为50X(比如502、504)时,自动重试。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix'">
|
||||
<td>PROXY Protocol</td>
|
||||
<td>
|
||||
@@ -12428,13 +12429,35 @@ Vue.component("http-remote-addr-config-box", {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
value: "${rawRemoteAddr}",
|
||||
isCustomized: false
|
||||
type: "default",
|
||||
|
||||
requestHeaderName: ""
|
||||
}
|
||||
}
|
||||
|
||||
let optionValue = ""
|
||||
if (!config.isCustomized && (config.value == "${remoteAddr}" || config.value == "${rawRemoteAddr}")) {
|
||||
optionValue = config.value
|
||||
// type
|
||||
if (config.type == null || config.type.length == 0) {
|
||||
config.type = "default"
|
||||
switch (config.value) {
|
||||
case "${rawRemoteAddr}":
|
||||
config.type = "default"
|
||||
break
|
||||
case "${remoteAddrValue}":
|
||||
config.type = "default"
|
||||
break
|
||||
case "${remoteAddr}":
|
||||
config.type = "proxy"
|
||||
break
|
||||
default:
|
||||
if (config.value != null && config.value.length > 0) {
|
||||
config.type = "variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// value
|
||||
if (config.value == null || config.value.length == 0) {
|
||||
config.value = "${rawRemoteAddr}"
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -12442,33 +12465,70 @@ Vue.component("http-remote-addr-config-box", {
|
||||
options: [
|
||||
{
|
||||
name: "直接获取",
|
||||
description: "用户直接访问边缘节点,即 \"用户 --> 边缘节点\" 模式,这时候可以直接从连接中读取到真实的IP地址。",
|
||||
value: "${rawRemoteAddr}"
|
||||
description: "用户直接访问边缘节点,即 \"用户 --> 边缘节点\" 模式,这时候系统会试图从直接的连接中读取到客户端IP地址。",
|
||||
value: "${rawRemoteAddr}",
|
||||
type: "default"
|
||||
},
|
||||
{
|
||||
name: "从上级代理中获取",
|
||||
description: "用户和边缘节点之间有别的代理服务转发,即 \"用户 --> [第三方代理服务] --> 边缘节点\",这时候只能从上级代理中获取传递的IP地址。",
|
||||
value: "${remoteAddr}"
|
||||
description: "用户和边缘节点之间有别的代理服务转发,即 \"用户 --> [第三方代理服务] --> 边缘节点\",这时候只能从上级代理中获取传递的IP地址;上级代理传递的请求报头中必须包含 X-Forwarded-For 或 X-Real-IP 信息。",
|
||||
value: "${remoteAddr}",
|
||||
type: "proxy"
|
||||
},
|
||||
{
|
||||
name: "[自定义]",
|
||||
name: "从请求报头中读取",
|
||||
description: "从自定义请求报头读取客户端IP。",
|
||||
value: "",
|
||||
type: "requestHeader"
|
||||
},
|
||||
{
|
||||
name: "[自定义变量]",
|
||||
description: "通过自定义变量来获取客户端真实的IP地址。",
|
||||
value: ""
|
||||
value: "",
|
||||
type: "variable"
|
||||
}
|
||||
],
|
||||
optionValue: optionValue
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"config.requestHeaderName": function (value) {
|
||||
if (this.config.type == "requestHeader"){
|
||||
this.config.value = "${header." + value.trim() + "}"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isOn: function () {
|
||||
return ((!this.vIsLocation && !this.vIsGroup) || this.config.isPrior) && this.config.isOn
|
||||
},
|
||||
changeOptionValue: function () {
|
||||
if (this.optionValue.length > 0) {
|
||||
this.config.value = this.optionValue
|
||||
this.config.isCustomized = false
|
||||
} else {
|
||||
this.config.isCustomized = true
|
||||
changeOptionType: function () {
|
||||
let that = this
|
||||
|
||||
switch(this.config.type) {
|
||||
case "default":
|
||||
this.config.value = "${rawRemoteAddr}"
|
||||
break
|
||||
case "proxy":
|
||||
this.config.value = "${remoteAddr}"
|
||||
break
|
||||
case "requestHeader":
|
||||
this.config.value = ""
|
||||
if (this.requestHeaderName != null && this.requestHeaderName.length > 0) {
|
||||
this.config.value = "${header." + this.requestHeaderName + "}"
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
that.$refs.requestHeaderInput.focus()
|
||||
})
|
||||
break
|
||||
case "variable":
|
||||
this.config.value = "${rawRemoteAddr}"
|
||||
|
||||
setTimeout(function () {
|
||||
that.$refs.variableInput.focus()
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12484,7 +12544,7 @@ Vue.component("http-remote-addr-config-box", {
|
||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后表示使用自定义的请求变量获取客户端IP。</p>
|
||||
<p class="comment">选中后,表示使用自定义的请求变量获取客户端IP。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -12492,20 +12552,28 @@ Vue.component("http-remote-addr-config-box", {
|
||||
<tr>
|
||||
<td>获取IP方式 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="optionValue" @change="changeOptionValue">
|
||||
<option v-for="option in options" :value="option.value">{{option.name}}</option>
|
||||
<select class="ui dropdown auto-width" v-model="config.type" @change="changeOptionType">
|
||||
<option v-for="option in options" :value="option.type">{{option.name}}</option>
|
||||
</select>
|
||||
<p class="comment" v-for="option in options" v-if="option.value == optionValue && option.description.length > 0">{{option.description}}</p>
|
||||
<p class="comment" v-for="option in options" v-if="option.type == config.type && option.description.length > 0">{{option.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="optionValue.length == 0">
|
||||
|
||||
<!-- read from request header -->
|
||||
<tr v-show="config.type == 'requestHeader'">
|
||||
<td>请求报头 *</td>
|
||||
<td>
|
||||
<input type="text" name="requestHeaderName" v-model="config.requestHeaderName" maxlength="100" ref="requestHeaderInput"/>
|
||||
<p class="comment">请输入包含有客户端IP的请求报头,需要注意大小写,常见的有<code-label>X-Forwarded-For</code-label>、<code-label>X-Real-IP</code-label>、<code-label>X-Client-IP</code-label>等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- read from variable -->
|
||||
<tr v-show="config.type == 'variable'">
|
||||
<td>读取IP变量值 *</td>
|
||||
<td>
|
||||
<input type="hidden" v-model="config.value" maxlength="100"/>
|
||||
<div v-if="optionValue == ''" style="margin-top: 1em">
|
||||
<input type="text" v-model="config.value" maxlength="100"/>
|
||||
<p class="comment">通过此变量获取用户的IP地址。具体可用的请求变量列表可参考官方网站文档。</p>
|
||||
</div>
|
||||
<input type="text" name="value" v-model="config.value" maxlength="100" ref="variableInput"/>
|
||||
<p class="comment">通过此变量获取用户的IP地址。具体可用的请求变量列表可参考官方网站文档;比如通过报头传递IP的情形,可以使用<code-label>\${header.你的自定义报头}</code-label>(类似于<code-label>\${header.X-Forwarded-For}</code-label>,需要注意大小写规范)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -12996,7 +13064,7 @@ Vue.component("http-webp-config-box", {
|
||||
quality: 50,
|
||||
minLength: {count: 0, "unit": "kb"},
|
||||
maxLength: {count: 0, "unit": "kb"},
|
||||
mimeTypes: ["image/png", "image/jpeg", "image/bmp", "image/x-ico", "image/gif"],
|
||||
mimeTypes: ["image/png", "image/jpeg", "image/bmp", "image/x-ico"],
|
||||
extensions: [".png", ".jpeg", ".jpg", ".bmp", ".ico"],
|
||||
conds: null
|
||||
}
|
||||
@@ -13062,7 +13130,7 @@ Vue.component("http-webp-config-box", {
|
||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后表示开启自动WebP压缩<span v-if="vRequireCache">;只有满足缓存条件的图片内容才会被转换</span>。</p>
|
||||
<p class="comment">选中后表示开启自动WebP压缩;图片的宽和高均不能超过16383像素<span v-if="vRequireCache">;只有满足缓存条件的图片内容才会被转换</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -13296,6 +13364,44 @@ Vue.component("http-oss-bucket-params", {
|
||||
</tbody>`
|
||||
})
|
||||
|
||||
Vue.component("http-request-scripts-config-box", {
|
||||
props: ["vRequestScriptsConfig", "v-is-location"],
|
||||
data: function () {
|
||||
let config = this.vRequestScriptsConfig
|
||||
if (config == null) {
|
||||
config = {}
|
||||
}
|
||||
return {
|
||||
config: config
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeInitGroup: function (group) {
|
||||
this.config.initGroup = group
|
||||
this.$forceUpdate()
|
||||
},
|
||||
changeRequestGroup: function (group) {
|
||||
this.config.requestGroup = group
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="requestScriptsJSON" :value="JSON.stringify(config)"/>
|
||||
<div class="margin"></div>
|
||||
<h4 style="margin-bottom: 0">请求初始化</h4>
|
||||
<p class="comment">在请求刚初始化时调用,此时自定义报头等尚未生效。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.initGroup" @change="changeInitGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<h4 style="margin-bottom: 0">准备发送请求</h4>
|
||||
<p class="comment">在准备执行请求或者转发请求之前调用,此时自定义报头、源站等已准备好。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.requestGroup" @change="changeRequestGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("http-request-cond-view", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
@@ -14923,7 +15029,8 @@ Vue.component("http-firewall-captcha-options", {
|
||||
return {
|
||||
options: options,
|
||||
isEditing: false,
|
||||
summary: ""
|
||||
summary: "",
|
||||
uiBodyWarning: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -14967,6 +15074,13 @@ Vue.component("http-firewall-captcha-options", {
|
||||
},
|
||||
"options.uiIsOn": function (v) {
|
||||
this.updateSummary()
|
||||
},
|
||||
"options.uiBody": function (v) {
|
||||
if (/<form(>|\s).+\$\{body}.*<\/form>/s.test(v)) {
|
||||
this.uiBodyWarning = "页面模板中不能使用<form></form>标签包裹\${body}变量,否则将导致验证码表单无法提交。"
|
||||
} else {
|
||||
this.uiBodyWarning = ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -15101,7 +15215,7 @@ Vue.component("http-firewall-captcha-options", {
|
||||
<td class="color-border">页面模板</td>
|
||||
<td>
|
||||
<textarea spellcheck="false" rows="2" v-model="options.uiBody"></textarea>
|
||||
<p class="comment"><span v-if="options.uiBody.length > 0 && options.uiBody.indexOf('\${body}') < 0 " class="red">模板中必须包含\${body}表示验证码表单!</span>整个页面的模板,支持HTML,其中必须使用<code-label>\${body}</code-label>变量代表验证码表单,否则将无法正常显示验证码。</p>
|
||||
<p class="comment"><span v-if="uiBodyWarning.length > 0" class="red">警告:{{uiBodyWarning}}</span><span v-if="options.uiBody.length > 0 && options.uiBody.indexOf('\${body}') < 0 " class="red">模板中必须包含\${body}表示验证码表单!</span>整个页面的模板,支持HTML,其中必须使用<code-label>\${body}</code-label>变量代表验证码表单,否则将无法正常显示验证码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -15677,10 +15791,13 @@ Vue.component("ip-list-table", {
|
||||
<keyword :v-word="keyword">{{item.ipFrom}}</keyword> <span> <span class="small red" v-if="item.isRead != null && !item.isRead"> New </span> <a :href="'/servers/iplists?ip=' + item.ipFrom" v-if="vShowSearchButton" title="搜索此IP"><span><i class="icon search small" style="color: #ccc"></i></span></a></span>
|
||||
<span v-if="item.ipTo.length > 0"> - <keyword :v-word="keyword">{{item.ipTo}}</keyword></span></span>
|
||||
<span v-else class="disabled">*</span>
|
||||
|
||||
<div v-if="item.region != null && item.region.length > 0">
|
||||
<span class="grey small">{{item.region}}</span>
|
||||
<span v-if="item.isp != null && item.isp.length > 0 && item.isp != '内网IP'" class="grey small"><span class="disabled">|</span> {{item.isp}}</span>
|
||||
</div>
|
||||
<div v-else-if="item.isp != null && item.isp.length > 0 && item.isp != '内网IP'"><span class="grey small">{{item.isp}}</span></div>
|
||||
|
||||
<div v-if="item.createdTime != null">
|
||||
<span class="small grey">添加于 {{item.createdTime}}
|
||||
<span v-if="item.list != null && item.list.id > 0">
|
||||
@@ -18209,7 +18326,7 @@ Vue.component("digit-input", {
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<input type="text" v-model="realValue" :maxlength="realMaxLength" :size="realSize" :class="{error: !this.isValid}" :placeholder="placeholder"/>`
|
||||
template: `<input type="text" v-model="realValue" :maxlength="realMaxLength" :size="realSize" :class="{error: !this.isValid}" :placeholder="placeholder" autocomplete="off"/>`
|
||||
})
|
||||
|
||||
Vue.component("keyword", {
|
||||
@@ -20534,6 +20651,9 @@ Vue.component("dns-route-selector", {
|
||||
this.routes.$removeIf(function (k, v) {
|
||||
return v.code + "@" + v.domainId == route.code + "@" + route.domainId
|
||||
})
|
||||
},
|
||||
clearKeyword: function () {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -20567,16 +20687,22 @@ Vue.component("dns-route-selector", {
|
||||
<tr>
|
||||
<td class="title">所有线路</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="routeCode">
|
||||
<option value="" v-if="keyword.length == 0">[请选择]</option>
|
||||
<option v-for="route in searchingRoutes" :value="route.code + '@' + route.domainId">{{route.name}}({{route.code}}/{{route.domainName}})</option>
|
||||
</select>
|
||||
<span v-if="keyword.length > 0 && searchingRoutes.length == 0">没有和关键词“{{keyword}}”匹配的线路</span>
|
||||
<span v-show="keyword.length == 0 || searchingRoutes.length > 0">
|
||||
<select class="ui dropdown" v-model="routeCode">
|
||||
<option value="" v-if="keyword.length == 0">[请选择]</option>
|
||||
<option v-for="route in searchingRoutes" :value="route.code + '@' + route.domainId">{{route.name}}({{route.code}}/{{route.domainName}})</option>
|
||||
</select>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>搜索</td>
|
||||
<td>搜索线路</td>
|
||||
<td>
|
||||
<input type="text" placeholder="搜索..." size="10" style="width: 10em" v-model="keyword" ref="keywordRef" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<div class="ui input" :class="{'right labeled':keyword.length > 0}">
|
||||
<input type="text" placeholder="线路名称或代号..." size="10" style="width: 10em" v-model="keyword" ref="keywordRef" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<a class="ui label" v-if="keyword.length > 0" @click.prevent="clearKeyword" href=""><i class="icon remove small blue"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -59,5 +59,5 @@ Vue.component("digit-input", {
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<input type="text" v-model="realValue" :maxlength="realMaxLength" :size="realSize" :class="{error: !this.isValid}" :placeholder="placeholder"/>`
|
||||
template: `<input type="text" v-model="realValue" :maxlength="realMaxLength" :size="realSize" :class="{error: !this.isValid}" :placeholder="placeholder" autocomplete="off"/>`
|
||||
})
|
||||
@@ -70,6 +70,9 @@ Vue.component("dns-route-selector", {
|
||||
this.routes.$removeIf(function (k, v) {
|
||||
return v.code + "@" + v.domainId == route.code + "@" + route.domainId
|
||||
})
|
||||
},
|
||||
clearKeyword: function () {
|
||||
this.keyword = ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -103,16 +106,22 @@ Vue.component("dns-route-selector", {
|
||||
<tr>
|
||||
<td class="title">所有线路</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="routeCode">
|
||||
<option value="" v-if="keyword.length == 0">[请选择]</option>
|
||||
<option v-for="route in searchingRoutes" :value="route.code + '@' + route.domainId">{{route.name}}({{route.code}}/{{route.domainName}})</option>
|
||||
</select>
|
||||
<span v-if="keyword.length > 0 && searchingRoutes.length == 0">没有和关键词“{{keyword}}”匹配的线路</span>
|
||||
<span v-show="keyword.length == 0 || searchingRoutes.length > 0">
|
||||
<select class="ui dropdown" v-model="routeCode">
|
||||
<option value="" v-if="keyword.length == 0">[请选择]</option>
|
||||
<option v-for="route in searchingRoutes" :value="route.code + '@' + route.domainId">{{route.name}}({{route.code}}/{{route.domainName}})</option>
|
||||
</select>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>搜索</td>
|
||||
<td>搜索线路</td>
|
||||
<td>
|
||||
<input type="text" placeholder="搜索..." size="10" style="width: 10em" v-model="keyword" ref="keywordRef" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<div class="ui input" :class="{'right labeled':keyword.length > 0}">
|
||||
<input type="text" placeholder="线路名称或代号..." size="10" style="width: 10em" v-model="keyword" ref="keywordRef" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<a class="ui label" v-if="keyword.length > 0" @click.prevent="clearKeyword" href=""><i class="icon remove small blue"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -118,10 +118,13 @@ Vue.component("ip-list-table", {
|
||||
<keyword :v-word="keyword">{{item.ipFrom}}</keyword> <span> <span class="small red" v-if="item.isRead != null && !item.isRead"> New </span> <a :href="'/servers/iplists?ip=' + item.ipFrom" v-if="vShowSearchButton" title="搜索此IP"><span><i class="icon search small" style="color: #ccc"></i></span></a></span>
|
||||
<span v-if="item.ipTo.length > 0"> - <keyword :v-word="keyword">{{item.ipTo}}</keyword></span></span>
|
||||
<span v-else class="disabled">*</span>
|
||||
|
||||
<div v-if="item.region != null && item.region.length > 0">
|
||||
<span class="grey small">{{item.region}}</span>
|
||||
<span v-if="item.isp != null && item.isp.length > 0 && item.isp != '内网IP'" class="grey small"><span class="disabled">|</span> {{item.isp}}</span>
|
||||
</div>
|
||||
<div v-else-if="item.isp != null && item.isp.length > 0 && item.isp != '内网IP'"><span class="grey small">{{item.isp}}</span></div>
|
||||
|
||||
<div v-if="item.createdTime != null">
|
||||
<span class="small grey">添加于 {{item.createdTime}}
|
||||
<span v-if="item.list != null && item.list.id > 0">
|
||||
|
||||
@@ -309,7 +309,7 @@ Vue.component("http-cache-ref-box", {
|
||||
<input type="checkbox" value="1" v-model="ref.skipSetCookie"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,当响应的Header中有Set-Cookie时不缓存响应内容。</p>
|
||||
<p class="comment">选中后,当响应的报头中有Set-Cookie时不缓存响应内容,防止动态内容被缓存。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
@@ -319,7 +319,7 @@ Vue.component("http-cache-ref-box", {
|
||||
<input type="checkbox" name="enableRequestCachePragma" value="1" v-model="ref.enableRequestCachePragma"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,当请求的Header中含有Pragma: no-cache或Cache-Control: no-cache时,会跳过缓存直接读取源内容。</p>
|
||||
<p class="comment">选中后,当请求的报头中含有Pragma: no-cache或Cache-Control: no-cache时,会跳过缓存直接读取源内容,一般仅用于调试。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
|
||||
@@ -54,11 +54,15 @@ Vue.component("http-firewall-actions-box", {
|
||||
}
|
||||
|
||||
var defaultPageBody = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<title>403 Forbidden</title>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
<body>
|
||||
<h1>403 Forbidden</h1>
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -673,7 +677,7 @@ Vue.component("http-firewall-actions-box", {
|
||||
<span v-if="config.code == 'post_307' && config.options.life > 0">:有效期{{config.options.life}}秒</span>
|
||||
|
||||
<!-- record_ip -->
|
||||
<span v-if="config.code == 'record_ip'">:{{config.options.ipListName}}</span>
|
||||
<span v-if="config.code == 'record_ip'">:<span :class="{red: config.options.ipListIsDeleted}">{{config.options.ipListName}}</span></span>
|
||||
|
||||
<!-- tag -->
|
||||
<span v-if="config.code == 'tag'">:{{config.options.tags.join(", ")}}</span>
|
||||
|
||||
@@ -30,7 +30,8 @@ Vue.component("http-firewall-captcha-options", {
|
||||
return {
|
||||
options: options,
|
||||
isEditing: false,
|
||||
summary: ""
|
||||
summary: "",
|
||||
uiBodyWarning: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -74,6 +75,13 @@ Vue.component("http-firewall-captcha-options", {
|
||||
},
|
||||
"options.uiIsOn": function (v) {
|
||||
this.updateSummary()
|
||||
},
|
||||
"options.uiBody": function (v) {
|
||||
if (/<form(>|\s).+\$\{body}.*<\/form>/s.test(v)) {
|
||||
this.uiBodyWarning = "页面模板中不能使用<form></form>标签包裹\${body}变量,否则将导致验证码表单无法提交。"
|
||||
} else {
|
||||
this.uiBodyWarning = ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -208,7 +216,7 @@ Vue.component("http-firewall-captcha-options", {
|
||||
<td class="color-border">页面模板</td>
|
||||
<td>
|
||||
<textarea spellcheck="false" rows="2" v-model="options.uiBody"></textarea>
|
||||
<p class="comment"><span v-if="options.uiBody.length > 0 && options.uiBody.indexOf('\${body}') < 0 " class="red">模板中必须包含\${body}表示验证码表单!</span>整个页面的模板,支持HTML,其中必须使用<code-label>\${body}</code-label>变量代表验证码表单,否则将无法正常显示验证码。</p>
|
||||
<p class="comment"><span v-if="uiBodyWarning.length > 0" class="red">警告:{{uiBodyWarning}}</span><span v-if="options.uiBody.length > 0 && options.uiBody.indexOf('\${body}') < 0 " class="red">模板中必须包含\${body}表示验证码表单!</span>整个页面的模板,支持HTML,其中必须使用<code-label>\${body}</code-label>变量代表验证码表单,否则将无法正常显示验证码。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -53,7 +53,7 @@ Vue.component("http-firewall-config-box", {
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible"></more-options-tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible" v-show="firewall.isOn"></more-options-tbody>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>启用系统全局规则</td>
|
||||
|
||||
@@ -71,17 +71,21 @@ Vue.component("http-pages-and-shutdown-box", {
|
||||
},
|
||||
addShutdownHTMLTemplate: function () {
|
||||
this.shutdownConfig.body = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
\t<title>升级中</title>
|
||||
\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>网站升级中</h1>
|
||||
<p>为了给您提供更好的服务,我们正在升级网站,请稍后重新访问。</p>
|
||||
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -7,13 +7,35 @@ Vue.component("http-remote-addr-config-box", {
|
||||
isPrior: false,
|
||||
isOn: false,
|
||||
value: "${rawRemoteAddr}",
|
||||
isCustomized: false
|
||||
type: "default",
|
||||
|
||||
requestHeaderName: ""
|
||||
}
|
||||
}
|
||||
|
||||
let optionValue = ""
|
||||
if (!config.isCustomized && (config.value == "${remoteAddr}" || config.value == "${rawRemoteAddr}")) {
|
||||
optionValue = config.value
|
||||
// type
|
||||
if (config.type == null || config.type.length == 0) {
|
||||
config.type = "default"
|
||||
switch (config.value) {
|
||||
case "${rawRemoteAddr}":
|
||||
config.type = "default"
|
||||
break
|
||||
case "${remoteAddrValue}":
|
||||
config.type = "default"
|
||||
break
|
||||
case "${remoteAddr}":
|
||||
config.type = "proxy"
|
||||
break
|
||||
default:
|
||||
if (config.value != null && config.value.length > 0) {
|
||||
config.type = "variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// value
|
||||
if (config.value == null || config.value.length == 0) {
|
||||
config.value = "${rawRemoteAddr}"
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -21,33 +43,70 @@ Vue.component("http-remote-addr-config-box", {
|
||||
options: [
|
||||
{
|
||||
name: "直接获取",
|
||||
description: "用户直接访问边缘节点,即 \"用户 --> 边缘节点\" 模式,这时候可以直接从连接中读取到真实的IP地址。",
|
||||
value: "${rawRemoteAddr}"
|
||||
description: "用户直接访问边缘节点,即 \"用户 --> 边缘节点\" 模式,这时候系统会试图从直接的连接中读取到客户端IP地址。",
|
||||
value: "${rawRemoteAddr}",
|
||||
type: "default"
|
||||
},
|
||||
{
|
||||
name: "从上级代理中获取",
|
||||
description: "用户和边缘节点之间有别的代理服务转发,即 \"用户 --> [第三方代理服务] --> 边缘节点\",这时候只能从上级代理中获取传递的IP地址。",
|
||||
value: "${remoteAddr}"
|
||||
description: "用户和边缘节点之间有别的代理服务转发,即 \"用户 --> [第三方代理服务] --> 边缘节点\",这时候只能从上级代理中获取传递的IP地址;上级代理传递的请求报头中必须包含 X-Forwarded-For 或 X-Real-IP 信息。",
|
||||
value: "${remoteAddr}",
|
||||
type: "proxy"
|
||||
},
|
||||
{
|
||||
name: "[自定义]",
|
||||
name: "从请求报头中读取",
|
||||
description: "从自定义请求报头读取客户端IP。",
|
||||
value: "",
|
||||
type: "requestHeader"
|
||||
},
|
||||
{
|
||||
name: "[自定义变量]",
|
||||
description: "通过自定义变量来获取客户端真实的IP地址。",
|
||||
value: ""
|
||||
value: "",
|
||||
type: "variable"
|
||||
}
|
||||
],
|
||||
optionValue: optionValue
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"config.requestHeaderName": function (value) {
|
||||
if (this.config.type == "requestHeader"){
|
||||
this.config.value = "${header." + value.trim() + "}"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isOn: function () {
|
||||
return ((!this.vIsLocation && !this.vIsGroup) || this.config.isPrior) && this.config.isOn
|
||||
},
|
||||
changeOptionValue: function () {
|
||||
if (this.optionValue.length > 0) {
|
||||
this.config.value = this.optionValue
|
||||
this.config.isCustomized = false
|
||||
} else {
|
||||
this.config.isCustomized = true
|
||||
changeOptionType: function () {
|
||||
let that = this
|
||||
|
||||
switch(this.config.type) {
|
||||
case "default":
|
||||
this.config.value = "${rawRemoteAddr}"
|
||||
break
|
||||
case "proxy":
|
||||
this.config.value = "${remoteAddr}"
|
||||
break
|
||||
case "requestHeader":
|
||||
this.config.value = ""
|
||||
if (this.requestHeaderName != null && this.requestHeaderName.length > 0) {
|
||||
this.config.value = "${header." + this.requestHeaderName + "}"
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
that.$refs.requestHeaderInput.focus()
|
||||
})
|
||||
break
|
||||
case "variable":
|
||||
this.config.value = "${rawRemoteAddr}"
|
||||
|
||||
setTimeout(function () {
|
||||
that.$refs.variableInput.focus()
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -63,7 +122,7 @@ Vue.component("http-remote-addr-config-box", {
|
||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后表示使用自定义的请求变量获取客户端IP。</p>
|
||||
<p class="comment">选中后,表示使用自定义的请求变量获取客户端IP。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -71,20 +130,28 @@ Vue.component("http-remote-addr-config-box", {
|
||||
<tr>
|
||||
<td>获取IP方式 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" v-model="optionValue" @change="changeOptionValue">
|
||||
<option v-for="option in options" :value="option.value">{{option.name}}</option>
|
||||
<select class="ui dropdown auto-width" v-model="config.type" @change="changeOptionType">
|
||||
<option v-for="option in options" :value="option.type">{{option.name}}</option>
|
||||
</select>
|
||||
<p class="comment" v-for="option in options" v-if="option.value == optionValue && option.description.length > 0">{{option.description}}</p>
|
||||
<p class="comment" v-for="option in options" v-if="option.type == config.type && option.description.length > 0">{{option.description}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="optionValue.length == 0">
|
||||
|
||||
<!-- read from request header -->
|
||||
<tr v-show="config.type == 'requestHeader'">
|
||||
<td>请求报头 *</td>
|
||||
<td>
|
||||
<input type="text" name="requestHeaderName" v-model="config.requestHeaderName" maxlength="100" ref="requestHeaderInput"/>
|
||||
<p class="comment">请输入包含有客户端IP的请求报头,需要注意大小写,常见的有<code-label>X-Forwarded-For</code-label>、<code-label>X-Real-IP</code-label>、<code-label>X-Client-IP</code-label>等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- read from variable -->
|
||||
<tr v-show="config.type == 'variable'">
|
||||
<td>读取IP变量值 *</td>
|
||||
<td>
|
||||
<input type="hidden" v-model="config.value" maxlength="100"/>
|
||||
<div v-if="optionValue == ''" style="margin-top: 1em">
|
||||
<input type="text" v-model="config.value" maxlength="100"/>
|
||||
<p class="comment">通过此变量获取用户的IP地址。具体可用的请求变量列表可参考官方网站文档。</p>
|
||||
</div>
|
||||
<input type="text" name="value" v-model="config.value" maxlength="100" ref="variableInput"/>
|
||||
<p class="comment">通过此变量获取用户的IP地址。具体可用的请求变量列表可参考官方网站文档;比如通过报头传递IP的情形,可以使用<code-label>\${header.你的自定义报头}</code-label>(类似于<code-label>\${header.X-Forwarded-For}</code-label>,需要注意大小写规范)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
Vue.component("http-request-scripts-config-box", {
|
||||
props: ["vRequestScriptsConfig", "v-is-location"],
|
||||
data: function () {
|
||||
let config = this.vRequestScriptsConfig
|
||||
if (config == null) {
|
||||
config = {}
|
||||
}
|
||||
return {
|
||||
config: config
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeInitGroup: function (group) {
|
||||
this.config.initGroup = group
|
||||
this.$forceUpdate()
|
||||
},
|
||||
changeRequestGroup: function (group) {
|
||||
this.config.requestGroup = group
|
||||
this.$forceUpdate()
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="requestScriptsJSON" :value="JSON.stringify(config)"/>
|
||||
<div class="margin"></div>
|
||||
<h4 style="margin-bottom: 0">请求初始化</h4>
|
||||
<p class="comment">在请求刚初始化时调用,此时自定义Header等尚未生效。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.initGroup" @change="changeInitGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<h4 style="margin-bottom: 0">准备发送请求</h4>
|
||||
<p class="comment">在准备执行请求或者转发请求之前调用,此时自定义Header、源站等已准备好。</p>
|
||||
<div>
|
||||
<script-group-config-box :v-group="config.requestGroup" @change="changeRequestGroup" :v-is-location="vIsLocation"></script-group-config-box>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
})
|
||||
@@ -9,7 +9,7 @@ Vue.component("http-webp-config-box", {
|
||||
quality: 50,
|
||||
minLength: {count: 0, "unit": "kb"},
|
||||
maxLength: {count: 0, "unit": "kb"},
|
||||
mimeTypes: ["image/png", "image/jpeg", "image/bmp", "image/x-ico", "image/gif"],
|
||||
mimeTypes: ["image/png", "image/jpeg", "image/bmp", "image/x-ico"],
|
||||
extensions: [".png", ".jpeg", ".jpg", ".bmp", ".ico"],
|
||||
conds: null
|
||||
}
|
||||
@@ -75,7 +75,7 @@ Vue.component("http-webp-config-box", {
|
||||
<input type="checkbox" value="1" v-model="config.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后表示开启自动WebP压缩<span v-if="vRequireCache">;只有满足缓存条件的图片内容才会被转换</span>。</p>
|
||||
<p class="comment">选中后表示开启自动WebP压缩;图片的宽和高均不能超过16383像素<span v-if="vRequireCache">;只有满足缓存条件的图片内容才会被转换</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -25,7 +25,8 @@ Vue.component("reverse-proxy-box", {
|
||||
idleTimeout: {count: 0, unit: "second"},
|
||||
maxConns: 0,
|
||||
maxIdleConns: 0,
|
||||
followRedirects: false
|
||||
followRedirects: false,
|
||||
retry50X: true
|
||||
}
|
||||
}
|
||||
if (reverseProxyConfig.addHeaders == null) {
|
||||
@@ -165,6 +166,7 @@ Vue.component("reverse-proxy-box", {
|
||||
<input type="checkbox" v-model="reverseProxyRef.isOn"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中后,所有源站设置才会生效。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
@@ -176,7 +178,7 @@ Vue.component("reverse-proxy-box", {
|
||||
<div v-show="reverseProxyConfig.requestHostType == 2" style="margin-top: 0.8em">
|
||||
<input type="text" placeholder="比如example.com" v-model="reverseProxyConfig.requestHost"/>
|
||||
</div>
|
||||
<p class="comment">请求源站时的Host,用于修改源站接收到的域名
|
||||
<p class="comment">请求源站时的主机名(Host),用于修改源站接收到的域名
|
||||
<span v-if="reverseProxyConfig.requestHostType == 0">,"跟随CDN域名"是指源站接收到的域名和当前CDN访问域名保持一致</span>
|
||||
<span v-if="reverseProxyConfig.requestHostType == 1">,"跟随源站"是指源站接收到的域名仍然是填写的源站地址中的信息,不随代理服务域名改变而改变</span>
|
||||
<span v-if="reverseProxyConfig.requestHostType == 2">,自定义Host内容中支持请求变量</span>。</p>
|
||||
@@ -199,7 +201,7 @@ Vue.component("reverse-proxy-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动添加的Header</td>
|
||||
<td>自动添加报头</td>
|
||||
<td>
|
||||
<div>
|
||||
<div style="width: 14em; float: left; margin-bottom: 1em" v-for="header in forwardHeaders" :key="header.name">
|
||||
@@ -207,7 +209,7 @@ Vue.component("reverse-proxy-box", {
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
<p class="comment">选中后,会自动向源站请求添加这些Header。</p>
|
||||
<p class="comment">选中后,会自动向源站请求添加这些报头,以便于源站获取客户端信息。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
@@ -298,6 +300,13 @@ Vue.component("reverse-proxy-box", {
|
||||
<p class="comment">源站保持等待的空闲超时时间,0表示使用默认时间。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family == null || family == 'http'">
|
||||
<td>自动重试50X</td>
|
||||
<td>
|
||||
<checkbox v-model="reverseProxyConfig.retry50X"></checkbox>
|
||||
<p class="comment">选中后,表示当源站返回状态码为50X(比如502、504)时,自动重试。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="family != 'unix'">
|
||||
<td>PROXY Protocol</td>
|
||||
<td>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// 显示流量限制说明
|
||||
Vue.component("traffic-limit-view", {
|
||||
props: ["v-traffic-limit"],
|
||||
data: function () {
|
||||
return {
|
||||
config: this.vTrafficLimit
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<div v-if="config.isOn">
|
||||
<span v-if="config.dailySize != null && config.dailySize.count > 0">日流量限制:{{config.dailySize.count}}{{config.dailySize.unit.toUpperCase()}}<br/></span>
|
||||
<span v-if="config.monthlySize != null && config.monthlySize.count > 0">月流量限制:{{config.monthlySize.count}}{{config.monthlySize.unit.toUpperCase()}}<br/></span>
|
||||
</div>
|
||||
<span v-else class="disabled">没有限制。</span>
|
||||
</div>`
|
||||
})
|
||||
@@ -60,6 +60,18 @@ Tea.context(function () {
|
||||
.params({})
|
||||
.success(function (resp) {
|
||||
this.globalMessageBadge = resp.data.count
|
||||
|
||||
// add dot to title
|
||||
let dots = "••• "
|
||||
if (typeof document.title == "string") {
|
||||
if (resp.data.count > 0) {
|
||||
if (!document.title.startsWith(dots)) {
|
||||
document.title = dots + document.title
|
||||
}
|
||||
} else if (document.title.startsWith(dots)) {
|
||||
document.title = document.title.substring(dots.length)
|
||||
}
|
||||
}
|
||||
})
|
||||
.done(function () {
|
||||
let delay = 6000
|
||||
|
||||
@@ -63,6 +63,7 @@ div.comment {
|
||||
padding-top: 0.4em;
|
||||
font-weight: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.8;
|
||||
}
|
||||
p.comment em,
|
||||
div.comment em {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":"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","file":"@layout_popup.css"}
|
||||
{"version":3,"sources":["@layout_popup.less"],"names":[],"mappings":"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","file":"@layout_popup.css"}
|
||||
@@ -69,6 +69,7 @@ p.comment, div.comment {
|
||||
padding-top: 0.4em;
|
||||
font-weight: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
p.comment em, div.comment em {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<td class="title">缓存磁盘容量</td>
|
||||
<td>
|
||||
<size-capacity-box :v-value="node.maxCacheDiskCapacity" :v-name="'maxCacheDiskCapacityJSON'"></size-capacity-box>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量后,将会自动尝试清理旧数据,0表示不限制。</p>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量或磁盘接近用尽时,将会自动尝试清理旧数据,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -14,23 +14,35 @@
|
||||
<td class="title color-border">禁止未绑定域名访问</td>
|
||||
<td>
|
||||
<checkbox name="httpAllMatchDomainStrictly" v-model="config.httpAll.matchDomainStrictly"></checkbox>
|
||||
<p class="comment">选中后,表示禁止未绑定的域名和IP访问。</p>
|
||||
<p class="comment">选中后,表示禁止未在网站绑定的域名和IP访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
|
||||
<tr>
|
||||
<td class="color-border">处理未绑定域名方式</td>
|
||||
<td>
|
||||
<radio name="httpAllDomainMismatchActionCode" :v-value="'page'" v-model="httpAllDomainMismatchActionCode">显示提示页面</radio>
|
||||
<radio name="httpAllDomainMismatchActionCode" :v-value="'close'" v-model="httpAllDomainMismatchActionCode">关闭连接</radio>
|
||||
<p class="comment" v-if="httpAllDomainMismatchActionCode == 'page'">显示提示内容。</p>
|
||||
<p class="comment" v-if="httpAllDomainMismatchActionCode == 'close'">直接关闭网络连接,不提示任何内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="config.httpAll.matchDomainStrictly && httpAllDomainMismatchActionCode == 'page'">
|
||||
<td class="color-border">提示页面状态码</td>
|
||||
<td>
|
||||
<input type="text" name="httpAllDomainMismatchActionStatusCode" v-model="httpAllDomainMismatchActionStatusCode" style="width: 4em" maxlength="3"/>
|
||||
<p class="comment">默认404。</p>
|
||||
<p class="comment">访问未绑定域名时的提示页面状态码,默认404。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">未绑定域名页面提示</td>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly && httpAllDomainMismatchActionCode == 'page'">
|
||||
<td class="color-border">提示页面内容</td>
|
||||
<td>
|
||||
<textarea rows="3" name="httpAllDomainMismatchActionContentHTML" v-model="httpAllDomainMismatchActionContentHTML"></textarea>
|
||||
<p class="comment">访问未绑定的域名时提示的文字,可以使用HTML;仅限于HTTP请求,不适于用HTTPS(HTTPS会提示证书错误)。</p>
|
||||
<p class="comment">访问未绑定的域名时提示页面内容,可以使用HTML;仅限于HTTP请求,不适于用HTTPS(HTTPS会提示证书错误)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">允许例外的域名</td>
|
||||
<td>
|
||||
@@ -181,6 +193,24 @@
|
||||
<p class="comment">选中后,所有请求优先发送到L2或者更高级别节点。默认不开启的情况下,只有可以缓存的内容请求才会发送到L2或者更高级别节点。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ln请求负载均衡方法</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="httpAllLnRequestSchedulingMethod" v-model="config.httpAll.lnRequestSchedulingMethod">
|
||||
<option value="urlMapping">URL映射</option>
|
||||
<option value="random">随机</option>
|
||||
</select>
|
||||
<p class="comment" v-if="config.httpAll.lnRequestSchedulingMethod == 'urlMapping'">当存在多个Ln节点时,将请求根据URL自动映射到某个固定的Ln节点。</p>
|
||||
<p class="comment" v-if="config.httpAll.lnRequestSchedulingMethod == 'random'">当存在多个Ln节点时,将请求随机发送到某个Ln节点。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>支持${serverAddr}变量</td>
|
||||
<td>
|
||||
<checkbox name="httpAllEnableServerAddrVariable" v-model="config.httpAll.enableServerAddrVariable"></checkbox>
|
||||
<p class="comment">支持在自定义页面中使用<code-label>${serverAddr}</code-label>变量,表示访客当前正在访问的服务器地址。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<span v-if="task.type == 'nodeLevelChanged'">同步L2节点</span>
|
||||
<span v-if="task.type == 'ddosProtectionChanged'">DDoS配置</span>
|
||||
<span v-if="task.type == 'userServersStateChanged'">用户网站状态</span>
|
||||
<span v-if="task.type != null && task.type.startsWith('ipListDeleted')">删除IP名单</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="task.isDone" class="red">{{task.error}}</span>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<!-- 边缘节点升级提醒 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && nodeUpgradeInfo.count > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
<a href="/clusters">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
<a href="/clusters">升级提醒:有 {{nodeUpgradeInfo.count}} 个边缘节点需要升级到 v{{nodeUpgradeInfo.version}} 版本,系统正在尝试自动升级,请耐心等待...</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- API节点升级提醒 -->
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<tr v-if="protocol == 'https' || protocol == 'tls'">
|
||||
<td>{{protocol.toUpperCase()}}证书</td>
|
||||
<td>
|
||||
<ssl-certs-box :v-single-mode="true" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书。'"></ssl-certs-box>
|
||||
<ssl-certs-box :v-single-mode="true" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书;除非源站有特殊需求,否则不需要添加。'"></ssl-certs-box>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<td class="title">单个域名 *</td>
|
||||
<td>
|
||||
<input type="text" name="serverName" ref="focus" maxlength="1024" v-model="serverName.name"/>
|
||||
<p class="comment">请输入单个域名,域名中<strong>不要</strong>包含<code-label>http://</code-label>或<code-label>https://</code-label>;也可以使用泛域名,比如<code-label>*.example.com</code-label>。</p>
|
||||
<p class="comment">请输入单个域名,比如<code-label>example.com</code-label>、<code-label>www.example.com</code-label>,<strong>不要</strong>包含<code-label>http://</code-label>或<code-label>https://</code-label>或端口号;也可以使用泛域名,比如<code-label>*.example.com</code-label>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="mode == 'multiple'">
|
||||
<td class="title">多个域名 *</td>
|
||||
<td>
|
||||
<textarea name="serverNames" ref="serverNames" v-model="multipleServerNames"></textarea>
|
||||
<p class="comment">每行一个域名,域名中<strong>不要</strong>包含<code-label>http://</code-label>或<code-label>https://</code-label>;也可以使用泛域名,比如<code-label>*.example.com</code-label>。</p>
|
||||
<p class="comment">每行一个域名,比如<code-label>example.com</code-label>、<code-label>www.example.com</code-label>,<strong>不要</strong>包含<code-label>http://</code-label>或<code-label>https://</code-label>或端口号;也可以使用泛域名,比如<code-label>*.example.com</code-label>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<tr v-show="reason == REASON_ISSUE_REPORT">
|
||||
<td>操作建议</td>
|
||||
<td>
|
||||
本产品提供的 <a href="/servers/components/cache/batch?keyType=prefix" target="_blank">[刷新预热]</a> 功能运行非常可靠,如果你确认发现功能异常,且能100%几率重现问题,请及时报告给软件开发者。
|
||||
当前系统提供的 <a href="/servers/components/cache/batch?keyType=prefix" target="_blank">[刷新预热]</a> 功能运行非常可靠,如果你确认发现功能异常,且能100%几率重现问题,请及时报告给软件开发者。
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="reason == REASON_BATCH_DELETE">
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<td class="color-border">缓存磁盘最大用量 *</td>
|
||||
<td>
|
||||
<size-capacity-box :v-name="'capacityJSON'" :v-count="128" :v-unit="'gb'" :key="'capacityJSON1'"></size-capacity-box>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量后,将会自动尝试清理旧数据,如果为0表示没有限制。</p>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量或磁盘接近用尽时,将会自动尝试清理旧数据,如果为0表示没有限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<td class="color-border">缓存磁盘最大用量</td>
|
||||
<td>
|
||||
<size-capacity-box :v-name="'capacityJSON'" :v-value="cachePolicy.capacity" :v-count="0" :v-unit="'gb'"></size-capacity-box>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量后,将会自动尝试清理旧数据,如果为0表示没有限制。</p>
|
||||
<p class="comment">缓存所在磁盘的最大用量,超出此用量或磁盘接近用尽时,将会自动尝试清理旧数据,如果为0表示没有限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="cachePolicy.options.memoryPolicy != null && cachePolicy.options.memoryPolicy.capacity != null">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<td>代号</td>
|
||||
<td>
|
||||
<input type="text" name="code" maxlength="100"/>
|
||||
<p class="comment">在导入时可以合并相同代号的分组。</p>
|
||||
<p class="comment">可选项,在导入时可以合并相同代号的分组。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<td>代号</td>
|
||||
<td>
|
||||
<input type="text" name="code" maxlength="100" v-model="group.code"/>
|
||||
<p class="comment">在导入时可以合并相同代号的分组。</p>
|
||||
<p class="comment">可选项,在导入时可以合并相同代号的分组。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<tr v-if="protocol == 'https' || protocol == 'tls'">
|
||||
<td>{{protocol.toUpperCase()}}证书</td>
|
||||
<td>
|
||||
<ssl-certs-box :v-single-mode="true" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书。'"></ssl-certs-box>
|
||||
<ssl-certs-box :v-single-mode="true" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书;除非源站有特殊需求,否则不需要添加。'"></ssl-certs-box>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="protocol != 'udp'">
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<tr v-if="origin.protocol == 'https' || origin.protocol == 'tls'">
|
||||
<td>{{origin.protocol.toUpperCase()}}证书</td>
|
||||
<td>
|
||||
<ssl-certs-box :v-single-mode="true" :v-cert="origin.cert" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书。'"></ssl-certs-box>
|
||||
<ssl-certs-box :v-single-mode="true" :v-cert="origin.cert" :v-view-size="'mini'" :v-description="'可以选择连接源站使用的证书;除非源站有特殊需求,否则不需要添加。'"></ssl-certs-box>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="origin.protocol != 'udp'">
|
||||
|
||||
@@ -3,17 +3,21 @@ Tea.context(function () {
|
||||
|
||||
this.addHTMLTemplate = function () {
|
||||
this.$refs.htmlBody.value = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
\t<title>\${status} \${statusMessage}</title>
|
||||
\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>\${status} \${statusMessage}</h1>
|
||||
<p><!-- 内容 --></p>
|
||||
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -10,17 +10,21 @@ Tea.context(function () {
|
||||
|
||||
this.addHTMLTemplate = function () {
|
||||
this.$refs.htmlBody.value = `<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
\t<title>\${status} \${statusMessage}</title>
|
||||
\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
\t<style>
|
||||
\t\taddress { line-height: 1.8; }
|
||||
\t</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>\${status} \${statusMessage}</h1>
|
||||
<p><!-- 内容 --></p>
|
||||
|
||||
<address>Request ID: \${requestId}.</address>
|
||||
<address>Connection: \${remoteAddr} (Client) -> \${serverAddr} (Server)</address>
|
||||
<address>Request ID: \${requestId}</address>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -54,6 +54,20 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 style="margin-top: 0">用户套餐带宽统计</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">数据保存天数 *</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" name="userPlanBandwidthStatCleanDays" v-model="config.userPlanBandwidthStat.clean.days" style="width:6em" maxlength="6"/>
|
||||
<span class="ui label">天</span>
|
||||
</div>
|
||||
<p class="comment"><pro-warning-label></pro-warning-label></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4 style="margin-top: 0">网站流量统计</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user