Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ff8aecbea | ||
|
|
e6cae84764 | ||
|
|
2d2d2931f1 | ||
|
|
69ad5e7d1f | ||
|
|
97abc6aeb9 | ||
|
|
8cf37dd37c | ||
|
|
e11d091ac5 | ||
|
|
e952dfcedd | ||
|
|
2565d5cab2 | ||
|
|
e4e66c74bc | ||
|
|
6607c41823 | ||
|
|
43570f20b5 | ||
|
|
9ea7a93206 | ||
|
|
dec4922fe5 | ||
|
|
ef1f95b347 | ||
|
|
7317312f68 | ||
|
|
2965e8df65 | ||
|
|
4fdcc7fae9 | ||
|
|
6be8f7539d | ||
|
|
a6de65fe39 | ||
|
|
fc5dae5a8e | ||
|
|
db69454d32 | ||
|
|
c824335b0e | ||
|
|
1f1ce11078 | ||
|
|
0ec1571d37 | ||
|
|
407e8b52a5 | ||
|
|
1be13a151d | ||
|
|
4d9d417d7a | ||
|
|
aaff8f0c4a | ||
|
|
8c064daf3f | ||
|
|
b0b3dae147 | ||
|
|
2ea2be43d3 | ||
|
|
81f7364e93 | ||
|
|
c530869530 | ||
|
|
586ccd90ec | ||
|
|
ece237c49f | ||
|
|
11da5ca98c |
@@ -1,9 +1,9 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "1.1.0"
|
||||
Version = "1.2.0"
|
||||
|
||||
APINodeVersion = "1.1.0"
|
||||
APINodeVersion = "1.2.0"
|
||||
|
||||
ProductName = "Edge Admin"
|
||||
ProcessName = "edge-admin"
|
||||
|
||||
@@ -51,3 +51,19 @@ func IsConnError(err error) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUnimplementedError 检查是否为未实现错误
|
||||
func IsUnimplementedError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
statusErr, ok := status.FromError(err)
|
||||
if ok {
|
||||
if statusErr.Code() == codes.Unimplemented {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (this *CheckUpdatesTask) Loop() error {
|
||||
var vMap = maps.NewMap(version)
|
||||
if vMap.GetString("code") == "admin" {
|
||||
var latestVersion = vMap.GetString("version")
|
||||
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
|
||||
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 && (len(config.IgnoredVersion) == 0 || stringutil.VersionCompare(latestVersion, config.IgnoredVersion) > 0) {
|
||||
teaconst.NewVersionCode = latestVersion
|
||||
teaconst.NewVersionDownloadURL = dlHost + vMap.GetString("url")
|
||||
return nil
|
||||
|
||||
56
internal/utils/taskutils/concurrent.go
Normal file
56
internal/utils/taskutils/concurrent.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package taskutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func RunConcurrent(tasks any, concurrent int, f func(task any)) error {
|
||||
if tasks == nil {
|
||||
return nil
|
||||
}
|
||||
var tasksValue = reflect.ValueOf(tasks)
|
||||
if tasksValue.Type().Kind() != reflect.Slice {
|
||||
return errors.New("ony works for slice")
|
||||
}
|
||||
|
||||
var countTasks = tasksValue.Len()
|
||||
if countTasks == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if concurrent <= 0 {
|
||||
concurrent = 8
|
||||
}
|
||||
if concurrent > countTasks {
|
||||
concurrent = countTasks
|
||||
}
|
||||
|
||||
var taskChan = make(chan any, countTasks)
|
||||
for i := 0; i < countTasks; i++ {
|
||||
taskChan <- tasksValue.Index(i).Interface()
|
||||
}
|
||||
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(concurrent)
|
||||
for i := 0; i < concurrent; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case task := <-taskChan:
|
||||
f(task)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
17
internal/utils/taskutils/concurrent_test.go
Normal file
17
internal/utils/taskutils/concurrent_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package taskutils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/taskutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunConcurrent(t *testing.T) {
|
||||
err := taskutils.RunConcurrent([]string{"a", "b", "c", "d", "e"}, 3, func(task any) {
|
||||
t.Log("run", task)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,19 @@ import (
|
||||
|
||||
// Fail 提示服务器错误信息
|
||||
func Fail(action actions.ActionWrapper, err error) {
|
||||
if err != nil {
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
if err == nil {
|
||||
err = errors.New("unknown error")
|
||||
}
|
||||
action.Object().Fail(teaconst.ErrServer + "(" + err.Error() + ")")
|
||||
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
|
||||
_, _, isLocalAPI, issuesHTML := parseAPIErr(action, err)
|
||||
if isLocalAPI && len(issuesHTML) > 0 {
|
||||
action.Object().Fail(teaconst.ErrServer + "(" + err.Error() + ";最近一次错误提示:" + issuesHTML + ")")
|
||||
} else {
|
||||
action.Object().Fail(teaconst.ErrServer + "(" + err.Error() + ")")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FailPage 提示页面错误信息
|
||||
@@ -38,67 +47,11 @@ func FailPage(action actions.ActionWrapper, err error) {
|
||||
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
|
||||
// 当前API终端地址
|
||||
var apiEndpoints = []string{}
|
||||
apiConfig, apiConfigErr := configs.LoadAPIConfig()
|
||||
if apiConfigErr == nil && apiConfig != nil {
|
||||
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
|
||||
}
|
||||
|
||||
var isRPCConnError bool
|
||||
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
|
||||
var apiNodeIsStarting = false
|
||||
var apiNodeProgress = ""
|
||||
if isRPCConnError {
|
||||
// API节点是否正在启动
|
||||
var sock = gosock.NewTmpSock("edge-api")
|
||||
reply, err := sock.SendTimeout(&gosock.Command{
|
||||
Code: "starting",
|
||||
Params: nil,
|
||||
}, 1*time.Second)
|
||||
if err == nil && reply != nil {
|
||||
var params = maps.NewMap(reply.Params)
|
||||
if params.GetBool("isStarting") {
|
||||
apiNodeIsStarting = true
|
||||
|
||||
var progressMap = params.GetMap("progress")
|
||||
apiNodeProgress = progressMap.GetString("description")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action.Object().ResponseWriter.WriteHeader(http.StatusInternalServerError)
|
||||
if len(action.Object().Request.Header.Get("X-Requested-With")) > 0 {
|
||||
action.Object().WriteString(teaconst.ErrServer)
|
||||
} else {
|
||||
// 本地的一些错误提示
|
||||
var isLocalAPI = false
|
||||
if isRPCConnError {
|
||||
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
|
||||
if hostErr == nil {
|
||||
for _, endpoint := range apiEndpoints {
|
||||
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
|
||||
isLocalAPI = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var issuesHTML = ""
|
||||
if isLocalAPI {
|
||||
// 读取本地API节点的issues
|
||||
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
|
||||
if issuesErr == nil {
|
||||
var issueMaps = []maps.Map{}
|
||||
issuesErr = json.Unmarshal(issuesData, &issueMaps)
|
||||
if issuesErr == nil && len(issueMaps) > 0 {
|
||||
var issueMap = issueMaps[0]
|
||||
issuesHTML = "本地API节点启动错误:" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiNodeIsStarting, apiNodeProgress, _, issuesHTML := parseAPIErr(action, err)
|
||||
var html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -187,3 +140,61 @@ func findStack(err string) string {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 分析API节点的错误信息
|
||||
func parseAPIErr(action actions.ActionWrapper, err error) (apiNodeIsStarting bool, apiNodeProgress string, isLocalAPI bool, issuesHTML string) {
|
||||
// 当前API终端地址
|
||||
var apiEndpoints = []string{}
|
||||
apiConfig, apiConfigErr := configs.LoadAPIConfig()
|
||||
if apiConfigErr == nil && apiConfig != nil {
|
||||
apiEndpoints = append(apiEndpoints, apiConfig.RPC.Endpoints...)
|
||||
}
|
||||
|
||||
var isRPCConnError bool
|
||||
err, isRPCConnError = rpcerrors.HumanError(err, apiEndpoints, Tea.ConfigFile("api.yaml"))
|
||||
if isRPCConnError {
|
||||
// API节点是否正在启动
|
||||
var sock = gosock.NewTmpSock("edge-api")
|
||||
reply, err := sock.SendTimeout(&gosock.Command{
|
||||
Code: "starting",
|
||||
Params: nil,
|
||||
}, 1*time.Second)
|
||||
if err == nil && reply != nil {
|
||||
var params = maps.NewMap(reply.Params)
|
||||
if params.GetBool("isStarting") {
|
||||
apiNodeIsStarting = true
|
||||
|
||||
var progressMap = params.GetMap("progress")
|
||||
apiNodeProgress = progressMap.GetString("description")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 本地的一些错误提示
|
||||
if isRPCConnError {
|
||||
host, _, hostErr := net.SplitHostPort(action.Object().Request.Host)
|
||||
if hostErr == nil {
|
||||
for _, endpoint := range apiEndpoints {
|
||||
if strings.HasPrefix(endpoint, "http://"+host) || strings.HasPrefix(endpoint, "https://"+host) || strings.HasPrefix(endpoint, host) {
|
||||
isLocalAPI = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isLocalAPI {
|
||||
// 读取本地API节点的issues
|
||||
issuesData, issuesErr := os.ReadFile(Tea.Root + "/edge-api/logs/issues.log")
|
||||
if issuesErr == nil {
|
||||
var issueMaps = []maps.Map{}
|
||||
issuesErr = json.Unmarshal(issuesData, &issueMaps)
|
||||
if issuesErr == nil && len(issueMaps) > 0 {
|
||||
var issueMap = issueMaps[0]
|
||||
issuesHTML = "本地API节点启动错误:" + issueMap.GetString("message") + ",处理建议:" + issueMap.GetString("suggestion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||
Helper(clusters.NewClusterHelper()).
|
||||
Data("teaMenu", "clusters").
|
||||
Data("teaSubMenu", "cluster").
|
||||
Prefix("/clusters/cluster").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/nodes", new(NodesAction)).
|
||||
|
||||
@@ -64,6 +64,7 @@ p { color: grey; }
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
this.Data["httpAllDomainMismatchActionContentHTML"] = httpAllDomainMismatchActionContentHTML
|
||||
|
||||
this.Show()
|
||||
@@ -77,6 +78,9 @@ func (this *IndexAction) RunPost(params struct {
|
||||
HttpAllAllowMismatchDomainsJSON []byte
|
||||
HttpAllAllowNodeIP bool
|
||||
HttpAllDefaultDomain string
|
||||
HttpAllNodeIPPageHTML string
|
||||
HttpAllNodeIPShowPage bool
|
||||
HttpAllForceLnRequest bool
|
||||
|
||||
HttpAllSupportsLowVersionHTTP bool
|
||||
HttpAllMatchCertFromAllServers bool
|
||||
@@ -136,10 +140,13 @@ func (this *IndexAction) RunPost(params struct {
|
||||
config.HTTPAll.AllowMismatchDomains = allowMismatchDomains
|
||||
config.HTTPAll.AllowNodeIP = params.HttpAllAllowNodeIP
|
||||
config.HTTPAll.DefaultDomain = params.HttpAllDefaultDomain
|
||||
config.HTTPAll.NodeIPShowPage = params.HttpAllNodeIPShowPage
|
||||
config.HTTPAll.NodeIPPageHTML = params.HttpAllNodeIPPageHTML
|
||||
|
||||
// HTTP All
|
||||
config.HTTPAll.SupportsLowVersionHTTP = params.HttpAllSupportsLowVersionHTTP
|
||||
config.HTTPAll.MatchCertFromAllServers = params.HttpAllMatchCertFromAllServers
|
||||
config.HTTPAll.ForceLnRequest = params.HttpAllForceLnRequest
|
||||
|
||||
// 访问日志
|
||||
config.HTTPAccessLog.EnableRequestHeaders = params.HttpAccessLogEnableRequestHeaders
|
||||
|
||||
@@ -24,6 +24,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||
Helper(clusters.NewClusterHelper()).
|
||||
Prefix("/clusters/cluster/settings").
|
||||
Data("teaSubMenu", "cluster").
|
||||
GetPost("", new(IndexAction)).
|
||||
|
||||
// 健康检查
|
||||
|
||||
@@ -13,6 +13,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||
Helper(clusterutils.NewClustersHelper()).
|
||||
Data("teaMenu", "clusters").
|
||||
Data("teaSubMenu", "cluster").
|
||||
Prefix("/clusters").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/create", new(CreateAction)).
|
||||
|
||||
@@ -16,6 +16,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeDNS)).
|
||||
Helper(new(Helper)).
|
||||
Prefix("/dns").
|
||||
Data("teaSubMenu", "cluster").
|
||||
Get("", new(IndexAction)).
|
||||
GetPost("/updateClusterPopup", new(UpdateClusterPopupAction)).
|
||||
Post("/providerOptions", new(ProviderOptionsAction)).
|
||||
|
||||
@@ -235,7 +235,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
func (this *IndexAction) checkRegion() bool {
|
||||
var ip = this.RequestRemoteIP()
|
||||
var result = iplibrary.LookupIP(ip)
|
||||
if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{10}, result.CountryId()) {
|
||||
if result != nil && result.IsOk() && result.CountryId() > 0 && lists.ContainsInt64([]int64{9, 10}, result.CountryId()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -27,6 +28,8 @@ func (this *AddOriginPopupAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["serverType"] = params.ServerType
|
||||
|
||||
this.getOSSHook()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -40,50 +43,93 @@ func (this *AddOriginPopupAction) RunPost(params struct {
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if !goNext {
|
||||
return
|
||||
}
|
||||
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
var portIndex = strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.Fail("地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
// 初始化
|
||||
var pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
var addrConfig = &serverconfigs.NetworkAddressConfig{
|
||||
Protocol: serverconfigs.Protocol(params.Protocol),
|
||||
}
|
||||
var ossJSON []byte
|
||||
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.Fail("端口号不能为0")
|
||||
}
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.Fail("端口号只能为整数")
|
||||
if ossConfig != nil { // OSS
|
||||
ossJSON, err = json.Marshal(ossConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
err = ossConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验OSS配置时出错:" + err.Error())
|
||||
return
|
||||
}
|
||||
} else { // 普通源站
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
}
|
||||
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
var portIndex = strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.Fail("地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.Fail("端口号不能为0")
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.Fail("端口号不能大于65535")
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.Fail("端口号只能为整数")
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
this.Fail("端口号不能为0")
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.Fail("端口号不能大于65535")
|
||||
}
|
||||
}
|
||||
|
||||
pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
|
||||
addrConfig = &serverconfigs.NetworkAddressConfig{
|
||||
Protocol: serverconfigs.Protocol(params.Protocol),
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,12 +149,9 @@ func (this *AddOriginPopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
resp, err := this.RPC().OriginRPC().CreateOrigin(this.AdminContext(), &pb.CreateOriginRequest{
|
||||
Name: "",
|
||||
Addr: &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
},
|
||||
Name: "",
|
||||
Addr: pbAddr,
|
||||
OssJSON: ossJSON,
|
||||
Description: "",
|
||||
Weight: 10,
|
||||
IsOn: true,
|
||||
@@ -124,14 +167,16 @@ func (this *AddOriginPopupAction) RunPost(params struct {
|
||||
var origin = &serverconfigs.OriginConfig{
|
||||
Id: resp.OriginId,
|
||||
IsOn: true,
|
||||
Addr: &serverconfigs.NetworkAddressConfig{
|
||||
Protocol: serverconfigs.Protocol(params.Protocol),
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
},
|
||||
Addr: addrConfig,
|
||||
OSS: ossConfig,
|
||||
}
|
||||
|
||||
this.Data["origin"] = origin
|
||||
this.Data["origin"] = maps.Map{
|
||||
"id": resp.OriginId,
|
||||
"isOn": true,
|
||||
"addr": addrConfig,
|
||||
"addrSummary": origin.AddrSummary(),
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建源站 %d", resp.OriginId)
|
||||
|
||||
20
internal/web/actions/default/servers/addOriginPopup_ext.go
Normal file
20
internal/web/actions/default/servers/addOriginPopup_ext.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package servers
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
func (this *AddOriginPopupAction) getOSSHook() {
|
||||
this.Data["ossTypes"] = []maps.Map{}
|
||||
this.Data["ossBucketParams"] = []maps.Map{}
|
||||
this.Data["ossForm"] = ""
|
||||
}
|
||||
|
||||
func (this *AddOriginPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -238,7 +238,8 @@ func (this *CreateAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
sslPolicyIdResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
|
||||
Http2Enabled: false, // 默认值
|
||||
Http2Enabled: false, // 默认值
|
||||
Http3Enabled: false,
|
||||
MinVersion: "TLS 1.1", // 默认值
|
||||
SslCertsJSON: certRefsJSON,
|
||||
HstsJSON: nil,
|
||||
@@ -387,7 +388,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
AdminId: this.AdminId(),
|
||||
Type: params.ServerType,
|
||||
Name: params.Name,
|
||||
ServerNamesJSON: params.ServerNames,
|
||||
ServerNamesJSON: params.ServerNames,
|
||||
Description: params.Description,
|
||||
NodeClusterId: clusterId,
|
||||
IncludeNodesJSON: includeNodesJSON,
|
||||
|
||||
@@ -42,7 +42,7 @@ func (this *CreatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建服务分组 %d", createResp.ServerGroupId)
|
||||
defer this.CreateLog(oplogs.LevelInfo, "创建网站分组 %d", createResp.ServerGroupId)
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func (this *DeleteAction) RunPost(params struct {
|
||||
GroupId int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "删除代理服务分组 %d", params.GroupId)
|
||||
defer this.CreateLog(oplogs.LevelInfo, "删除代理网站分组 %d", params.GroupId)
|
||||
|
||||
// 检查是否正在使用
|
||||
countResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithServerGroupId(this.AdminContext(), &pb.CountAllEnabledServersWithServerGroupIdRequest{ServerGroupId: params.GroupId})
|
||||
|
||||
@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
|
||||
@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
m := maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
m := maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
|
||||
@@ -59,7 +59,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
@@ -76,7 +77,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
|
||||
@@ -41,7 +41,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
Must *actions.Must
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改代理服务分组 %d", params.GroupId)
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改网站分组 %d", params.GroupId)
|
||||
|
||||
params.Must.
|
||||
Field("name", params.Name).
|
||||
|
||||
@@ -14,7 +14,7 @@ func (this *SortAction) RunPost(params struct {
|
||||
GroupIds []int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改服务分组排序")
|
||||
defer this.CreateLog(oplogs.LevelInfo, "修改网站分组排序")
|
||||
|
||||
_, err := this.RPC().ServerGroupRPC().UpdateServerGroupOrders(this.AdminContext(), &pb.UpdateServerGroupOrdersRequest{ServerGroupIds: params.GroupIds})
|
||||
if err != nil {
|
||||
|
||||
@@ -21,6 +21,7 @@ func (this *IndexAction) Init() {
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
Ip string
|
||||
Keyword string
|
||||
GlobalOnly bool
|
||||
Unread bool
|
||||
@@ -40,6 +41,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
Unread: true,
|
||||
EventLevel: params.EventLevel,
|
||||
ListType: params.ListType,
|
||||
Ip: params.Ip,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -53,6 +55,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
Unread: params.Unread,
|
||||
EventLevel: params.EventLevel,
|
||||
ListType: params.ListType,
|
||||
Ip: params.Ip,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -68,6 +71,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
Unread: params.Unread,
|
||||
EventLevel: params.EventLevel,
|
||||
ListType: params.ListType,
|
||||
Ip: params.Ip,
|
||||
Offset: page.Offset,
|
||||
Size: page.Size,
|
||||
})
|
||||
|
||||
@@ -85,11 +85,23 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 当前集群是否支持HTTP/3
|
||||
if server.NodeCluster == nil {
|
||||
this.ErrorPage(errors.New("no node cluster for the server"))
|
||||
return
|
||||
}
|
||||
supportsHTTP3, err := this.checkSupportsHTTP3(server.NodeCluster.Id)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["serverType"] = server.Type
|
||||
this.Data["httpsConfig"] = maps.Map{
|
||||
"isOn": httpsConfig.IsOn,
|
||||
"addresses": httpsConfig.Listen,
|
||||
"sslPolicy": sslPolicy,
|
||||
"isOn": httpsConfig.IsOn,
|
||||
"addresses": httpsConfig.Listen,
|
||||
"sslPolicy": sslPolicy,
|
||||
"supportsHTTP3": supportsHTTP3,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
@@ -175,6 +187,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
|
||||
SslPolicyId: sslPolicyId,
|
||||
Http2Enabled: sslPolicy.HTTP2Enabled,
|
||||
Http3Enabled: sslPolicy.HTTP3Enabled,
|
||||
MinVersion: sslPolicy.MinVersion,
|
||||
SslCertsJSON: certsJSON,
|
||||
HstsJSON: hstsJSON,
|
||||
@@ -191,6 +204,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
} else {
|
||||
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
|
||||
Http2Enabled: sslPolicy.HTTP2Enabled,
|
||||
Http3Enabled: sslPolicy.HTTP3Enabled,
|
||||
MinVersion: sslPolicy.MinVersion,
|
||||
SslCertsJSON: certsJSON,
|
||||
HstsJSON: hstsJSON,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package https
|
||||
|
||||
func (this *IndexAction) checkSupportsHTTP3(clusterId int64) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
@@ -60,7 +60,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
@@ -77,7 +78,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
|
||||
@@ -47,6 +47,9 @@ func (this *AddPopupAction) RunGet(params struct {
|
||||
// 是否为HTTP
|
||||
this.Data["isHTTP"] = serverType == "httpProxy" || serverType == "httpWeb"
|
||||
|
||||
// OSS
|
||||
this.getOSSHook()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -76,104 +79,142 @@ func (this *AddPopupAction) RunPost(params struct {
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
}
|
||||
|
||||
addr = strings.ReplaceAll(addr, ":", ":")
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
var portIndex = strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.FailField("addr", "源站地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.FailField("addr", "源站端口号只能为整数")
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.FailField("addr", "源站端口号不能大于65535")
|
||||
}
|
||||
}
|
||||
|
||||
connTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.ConnTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
readTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.ReadTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
if !goNext {
|
||||
return
|
||||
}
|
||||
|
||||
idleTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.IdleTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
// 初始化
|
||||
var pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
}
|
||||
var connTimeoutJSON []byte
|
||||
var readTimeoutJSON []byte
|
||||
var idleTimeoutJSON []byte
|
||||
var certRefJSON []byte
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
var ossJSON []byte = nil
|
||||
if ossConfig != nil { // OSS
|
||||
ossJSON, err = json.Marshal(ossConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
var certRefJSON []byte
|
||||
if len(certIds) > 0 {
|
||||
var certId = certIds[0]
|
||||
if certId > 0 {
|
||||
var certRef = &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
err = ossConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验OSS配置时出错:" + err.Error())
|
||||
return
|
||||
}
|
||||
} else { // 普通源站
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
certRefJSON, err = json.Marshal(certRef)
|
||||
}
|
||||
|
||||
addr = strings.ReplaceAll(addr, ":", ":")
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
var portIndex = strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.FailField("addr", "源站地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.FailField("addr", "源站端口号只能为整数")
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.FailField("addr", "源站端口号不能大于65535")
|
||||
}
|
||||
}
|
||||
|
||||
connTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.ConnTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
readTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.ReadTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
idleTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.IdleTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(certIds) > 0 {
|
||||
var certId = certIds[0]
|
||||
if certId > 0 {
|
||||
var certRef = &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
}
|
||||
certRefJSON, err = json.Marshal(certRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
}
|
||||
|
||||
// 专属域名
|
||||
@@ -192,12 +233,9 @@ func (this *AddPopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
createResp, err := this.RPC().OriginRPC().CreateOrigin(this.AdminContext(), &pb.CreateOriginRequest{
|
||||
Name: params.Name,
|
||||
Addr: &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
},
|
||||
Name: params.Name,
|
||||
Addr: pbAddr,
|
||||
OssJSON: ossJSON,
|
||||
Description: params.Description,
|
||||
Weight: params.Weight,
|
||||
IsOn: params.IsOn,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package origins
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
func (this *AddPopupAction) getOSSHook() {
|
||||
this.Data["ossTypes"] = []maps.Map{}
|
||||
this.Data["ossBucketParams"] = []maps.Map{}
|
||||
this.Data["ossForm"] = ""
|
||||
}
|
||||
|
||||
func (this *AddPopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -94,10 +94,14 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
config.Cert.KeyData = nil
|
||||
}
|
||||
|
||||
var addr = ""
|
||||
if len(config.Addr.Host) > 0 && len(config.Addr.PortRange) > 0 {
|
||||
addr = config.Addr.Host + ":" + config.Addr.PortRange
|
||||
}
|
||||
this.Data["origin"] = maps.Map{
|
||||
"id": config.Id,
|
||||
"protocol": config.Addr.Protocol,
|
||||
"addr": config.Addr.Host + ":" + config.Addr.PortRange,
|
||||
"addr": addr,
|
||||
"weight": config.Weight,
|
||||
"name": config.Name,
|
||||
"description": config.Description,
|
||||
@@ -111,8 +115,12 @@ func (this *UpdatePopupAction) RunGet(params struct {
|
||||
"domains": config.Domains,
|
||||
"host": config.RequestHost,
|
||||
"followPort": config.FollowPort,
|
||||
"oss": config.OSS,
|
||||
}
|
||||
|
||||
// OSS
|
||||
this.getOSSHook()
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -143,103 +151,144 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
}
|
||||
|
||||
addr = strings.ReplaceAll(addr, ":", ":")
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
portIndex := strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.FailField("addr", "源站地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.FailField("addr", "源站端口号只能为整数")
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.FailField("addr", "源站端口号不能大于65535")
|
||||
}
|
||||
}
|
||||
|
||||
connTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.ConnTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
ossConfig, goNext, err := this.postOSSHook(params.Protocol)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
readTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.ReadTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
if !goNext {
|
||||
return
|
||||
}
|
||||
|
||||
idleTimeoutJSON, err := (&shared.TimeDuration{
|
||||
Count: int64(params.IdleTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
var ossJSON []byte = nil
|
||||
var connTimeoutJSON []byte
|
||||
var readTimeoutJSON []byte
|
||||
var idleTimeoutJSON []byte
|
||||
var certRefJSON []byte
|
||||
var pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
}
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if ossConfig != nil { // OSS
|
||||
ossJSON, err = json.Marshal(ossConfig)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
var certRefJSON []byte
|
||||
if len(certIds) > 0 {
|
||||
var certId = certIds[0]
|
||||
if certId > 0 {
|
||||
var certRef = &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
err = ossConfig.Init()
|
||||
if err != nil {
|
||||
this.Fail("校验OSS配置时出错:" + err.Error())
|
||||
return
|
||||
}
|
||||
} else { // 普通源站
|
||||
params.Must.
|
||||
Field("addr", params.Addr).
|
||||
Require("请输入源站地址")
|
||||
|
||||
var addr = params.Addr
|
||||
|
||||
// 是否是完整的地址
|
||||
if (params.Protocol == "http" || params.Protocol == "https") && regexp.MustCompile(`^(http|https)://`).MatchString(addr) {
|
||||
u, err := url.Parse(addr)
|
||||
if err == nil {
|
||||
addr = u.Host
|
||||
}
|
||||
certRefJSON, err = json.Marshal(certRef)
|
||||
}
|
||||
|
||||
addr = strings.ReplaceAll(addr, ":", ":")
|
||||
addr = regexp.MustCompile(`\s+`).ReplaceAllString(addr, "")
|
||||
portIndex := strings.LastIndex(addr, ":")
|
||||
if portIndex < 0 {
|
||||
if params.Protocol == "http" {
|
||||
addr += ":80"
|
||||
} else if params.Protocol == "https" {
|
||||
addr += ":443"
|
||||
} else {
|
||||
this.FailField("addr", "源站地址中需要带有端口")
|
||||
}
|
||||
portIndex = strings.LastIndex(addr, ":")
|
||||
}
|
||||
var host = addr[:portIndex]
|
||||
var port = addr[portIndex+1:]
|
||||
// 检查端口号
|
||||
if port == "0" {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
return
|
||||
}
|
||||
if !configutils.HasVariables(port) {
|
||||
// 必须是整数
|
||||
if !regexp.MustCompile(`^\d+$`).MatchString(port) {
|
||||
this.FailField("addr", "源站端口号只能为整数")
|
||||
return
|
||||
}
|
||||
var portInt = types.Int(port)
|
||||
if portInt == 0 {
|
||||
this.FailField("addr", "源站端口号不能为0")
|
||||
return
|
||||
}
|
||||
if portInt > 65535 {
|
||||
this.FailField("addr", "源站端口号不能大于65535")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pbAddr = &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
}
|
||||
|
||||
connTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.ConnTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
readTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.ReadTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
idleTimeoutJSON, err = (&shared.TimeDuration{
|
||||
Count: int64(params.IdleTimeout),
|
||||
Unit: shared.TimeDurationUnitSecond,
|
||||
}).AsJSON()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 证书
|
||||
var certIds = []int64{}
|
||||
if len(params.CertIdsJSON) > 0 {
|
||||
err = json.Unmarshal(params.CertIdsJSON, &certIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(certIds) > 0 {
|
||||
var certId = certIds[0]
|
||||
if certId > 0 {
|
||||
var certRef = &sslconfigs.SSLCertRef{
|
||||
IsOn: true,
|
||||
CertId: certId,
|
||||
}
|
||||
certRefJSON, err = json.Marshal(certRef)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 专属域名
|
||||
@@ -258,13 +307,10 @@ func (this *UpdatePopupAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
_, err = this.RPC().OriginRPC().UpdateOrigin(this.AdminContext(), &pb.UpdateOriginRequest{
|
||||
OriginId: params.OriginId,
|
||||
Name: params.Name,
|
||||
Addr: &pb.NetworkAddress{
|
||||
Protocol: params.Protocol,
|
||||
Host: host,
|
||||
PortRange: port,
|
||||
},
|
||||
OriginId: params.OriginId,
|
||||
Name: params.Name,
|
||||
Addr: pbAddr,
|
||||
OssJSON: ossJSON,
|
||||
Description: params.Description,
|
||||
Weight: params.Weight,
|
||||
IsOn: params.IsOn,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build !plus
|
||||
|
||||
package origins
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
func (this *UpdatePopupAction) getOSSHook() {
|
||||
this.Data["ossTypes"] = []maps.Map{}
|
||||
this.Data["ossBucketParams"] = []maps.Map{}
|
||||
this.Data["ossForm"] = ""
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) postOSSHook(protocol string) (config *ossconfigs.OSSConfig, goNext bool, err error) {
|
||||
goNext = true
|
||||
return
|
||||
}
|
||||
@@ -87,7 +87,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
@@ -104,7 +105,8 @@ func (this *IndexAction) RunGet(params struct {
|
||||
var m = maps.Map{
|
||||
"id": originConfig.Id,
|
||||
"weight": originConfig.Weight,
|
||||
"addr": originConfig.Addr.Protocol.String() + "://" + originConfig.Addr.Host + ":" + originConfig.Addr.PortRange,
|
||||
"addr": originConfig.AddrSummary(),
|
||||
"isOSS": originConfig.IsOSS(),
|
||||
"name": originConfig.Name,
|
||||
"isOn": originConfig.IsOn,
|
||||
"domains": originConfig.Domains,
|
||||
|
||||
@@ -128,6 +128,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
_, err := this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{
|
||||
SslPolicyId: sslPolicyId,
|
||||
Http2Enabled: sslPolicy.HTTP2Enabled,
|
||||
Http3Enabled: sslPolicy.HTTP3Enabled,
|
||||
MinVersion: sslPolicy.MinVersion,
|
||||
SslCertsJSON: certsJSON,
|
||||
HstsJSON: hstsJSON,
|
||||
@@ -144,6 +145,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
} else {
|
||||
resp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{
|
||||
Http2Enabled: sslPolicy.HTTP2Enabled,
|
||||
Http3Enabled: sslPolicy.HTTP3Enabled,
|
||||
MinVersion: sslPolicy.MinVersion,
|
||||
SslCertsJSON: certsJSON,
|
||||
HstsJSON: hstsJSON,
|
||||
|
||||
@@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/api/node"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/api/node"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
@@ -14,7 +14,7 @@ func init() {
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(NewHelper()).
|
||||
Helper(settingutils.NewAdvancedHelper("apiNodes")).
|
||||
Prefix("/api").
|
||||
Prefix("/settings/api").
|
||||
Get("", new(IndexAction)).
|
||||
Get("/methodStats", new(MethodStatsAction)).
|
||||
GetPost("/node/createPopup", new(node.CreatePopupAction)).
|
||||
@@ -12,7 +12,7 @@ func init() {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
|
||||
Helper(settingutils.NewAdvancedHelper("apiNodes")).
|
||||
Prefix("/api/node").
|
||||
Prefix("/settings/api/node").
|
||||
|
||||
// 这里不受Helper的约束
|
||||
GetPost("/createAddrPopup", new(CreateAddrPopupAction)).
|
||||
@@ -33,7 +33,7 @@ func (this *AdvancedHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNex
|
||||
var adminId = session.GetInt64("adminId")
|
||||
if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) {
|
||||
tabbar.Add("数据库", "", "/settings/database", "", this.tab == "database")
|
||||
tabbar.Add("API节点", "", "/api", "", this.tab == "apiNodes")
|
||||
tabbar.Add("API节点", "", "/settings/api", "", this.tab == "apiNodes")
|
||||
tabbar.Add("日志数据库", "", "/db", "", this.tab == "dbNodes")
|
||||
tabbar.Add("迁移", "", "/settings/transfer", "", this.tab == "transfer")
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool)
|
||||
|
||||
// 左侧菜单
|
||||
action.Data["teaMenu"] = "settings"
|
||||
action.Data["teaSubMenu"] = "basic"
|
||||
|
||||
// 标签栏
|
||||
var tabbar = actionutils.NewTabbar()
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
)
|
||||
|
||||
type IgnoreVersionAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *IgnoreVersionAction) RunPost(params struct {
|
||||
Version string
|
||||
}) {
|
||||
defer this.CreateLogInfo("忽略升级版本 %s", params.Version)
|
||||
|
||||
if len(params.Version) == 0 {
|
||||
this.Fail("请输入要忽略的版本号")
|
||||
return
|
||||
}
|
||||
|
||||
valueResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
config.IgnoredVersion = params.Version
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||
Code: systemconfigs.SettingCodeCheckUpdates,
|
||||
ValueJSON: configJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 清除状态
|
||||
teaconst.NewVersionCode = ""
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -4,12 +4,12 @@ package updates
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -25,8 +25,18 @@ func (this *IndexAction) Init() {
|
||||
this.Nav("", "updates", "")
|
||||
}
|
||||
|
||||
func (this *IndexAction) RunGet(params struct{}) {
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
DoCheck bool
|
||||
}) {
|
||||
this.Data["version"] = teaconst.Version
|
||||
this.Data["doCheck"] = params.DoCheck
|
||||
|
||||
// 是否正在升级
|
||||
this.Data["isUpgrading"] = isUpgrading
|
||||
this.Data["upgradeProgress"] = fmt.Sprintf("%.2f", upgradeProgress * 100)
|
||||
if isUpgrading {
|
||||
this.Data["doCheck"] = false
|
||||
}
|
||||
|
||||
valueResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
|
||||
if err != nil {
|
||||
@@ -34,7 +44,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
return
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = &systemconfigs.CheckUpdatesConfig{AutoCheck: false}
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
@@ -49,6 +59,21 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
|
||||
func (this *IndexAction) RunPost(params struct {
|
||||
}) {
|
||||
valueResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
@@ -66,6 +91,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
"message": "读取更新信息失败:" + err.Error(),
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -78,6 +104,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
"message": "读取更新信息失败:" + err.Error(),
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var apiResponse = &Response{}
|
||||
@@ -88,6 +115,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
"message": "解析更新信息失败:" + err.Error(),
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
if apiResponse.Code != 200 {
|
||||
@@ -96,6 +124,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
"message": "解析更新信息失败:" + apiResponse.Message,
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var m = maps.NewMap(apiResponse.Data)
|
||||
@@ -107,19 +136,30 @@ func (this *IndexAction) RunPost(params struct {
|
||||
if vMap.GetString("code") == "admin" {
|
||||
var latestVersion = vMap.GetString("version")
|
||||
if stringutil.VersionCompare(teaconst.Version, latestVersion) < 0 {
|
||||
// 是否已忽略
|
||||
if len(config.IgnoredVersion) > 0 && stringutil.VersionCompare(config.IgnoredVersion, latestVersion) >= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": true,
|
||||
"message": "有最新的版本v" + types.String(latestVersion) + "可以更新",
|
||||
"hasNew": true,
|
||||
"dlURL": dlHost + vMap.GetString("url"),
|
||||
"isOk": true,
|
||||
"version": latestVersion,
|
||||
"message": "有最新的版本 v" + latestVersion + " 可以更新",
|
||||
"hasNew": true,
|
||||
"dlURL": dlHost + vMap.GetString("url"),
|
||||
"day": vMap.GetString("day"),
|
||||
"description": vMap.GetString("description"),
|
||||
"docURL": vMap.GetString("docURL"),
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
} else {
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": true,
|
||||
"message": "你已安装最新版本,无需更新",
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +167,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
|
||||
this.Data["result"] = maps.Map{
|
||||
"isOk": false,
|
||||
"message": "找不到更新信息",
|
||||
"message": "没有发现更新的版本",
|
||||
}
|
||||
|
||||
this.Success()
|
||||
|
||||
@@ -15,6 +15,9 @@ func init() {
|
||||
Prefix("/settings/updates").
|
||||
GetPost("", new(IndexAction)).
|
||||
Post("/update", new(UpdateAction)).
|
||||
Post("/ignoreVersion", new(IgnoreVersionAction)).
|
||||
Post("/resetIgnoredVersion", new(ResetIgnoredVersionAction)).
|
||||
GetPost("/upgrade", new(UpgradeAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
|
||||
)
|
||||
|
||||
type ResetIgnoredVersionAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *ResetIgnoredVersionAction) RunPost(params struct{}) {
|
||||
defer this.CreateLogInfo("重置忽略升级版本")
|
||||
|
||||
valueResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
config.IgnoredVersion = ""
|
||||
configJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||
Code: systemconfigs.SettingCodeCheckUpdates,
|
||||
ValueJSON: configJSON,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -17,13 +17,16 @@ type UpdateAction struct {
|
||||
func (this *UpdateAction) RunPost(params struct {
|
||||
AutoCheck bool
|
||||
}) {
|
||||
defer this.CreateLogInfo("修改检查更新设置")
|
||||
|
||||
// 读取当前设置
|
||||
valueResp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeCheckUpdates})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var valueJSON = valueResp.ValueJSON
|
||||
var config = &systemconfigs.CheckUpdatesConfig{AutoCheck: false}
|
||||
var config = systemconfigs.NewCheckUpdatesConfig()
|
||||
if len(valueJSON) > 0 {
|
||||
err = json.Unmarshal(valueJSON, config)
|
||||
if err != nil {
|
||||
@@ -40,6 +43,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
// 修改设置
|
||||
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
|
||||
Code: systemconfigs.SettingCodeCheckUpdates,
|
||||
ValueJSON: configJSON,
|
||||
|
||||
73
internal/web/actions/default/settings/updates/upgrade.go
Normal file
73
internal/web/actions/default/settings/updates/upgrade.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
var upgradeProgress float32
|
||||
var isUpgrading = false
|
||||
|
||||
type UpgradeAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["isUpgrading"] = isUpgrading
|
||||
this.Data["upgradeProgress"] = fmt.Sprintf("%.2f", upgradeProgress*100)
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func (this *UpgradeAction) RunPost(params struct {
|
||||
Url string
|
||||
}) {
|
||||
if isUpgrading {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
isUpgrading = true
|
||||
upgradeProgress = 0
|
||||
|
||||
defer func() {
|
||||
isUpgrading = false
|
||||
}()
|
||||
|
||||
var manager = utils.NewUpgradeManager("admin", params.Url)
|
||||
var ticker = time.NewTicker(1 * time.Second)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
if manager.IsDownloading() {
|
||||
var progress = manager.Progress()
|
||||
if progress >= 0 {
|
||||
upgradeProgress = progress
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := manager.Start()
|
||||
if err != nil {
|
||||
this.Fail("下载失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// restart
|
||||
exe, _ := os.Executable()
|
||||
if len(exe) > 0 {
|
||||
go func() {
|
||||
var cmd = exec.Command(exe, "restart")
|
||||
_ = cmd.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -80,12 +80,14 @@ func (this *IndexAction) RunPost(params struct {
|
||||
config.RPC.Endpoints = []string{endpoint}
|
||||
client, err := rpc.NewRPCClient(config, false)
|
||||
if err != nil {
|
||||
this.Fail("无法连接到API节点地址'" + endpoint + "':" + err.Error())
|
||||
actionutils.Fail(this, err)
|
||||
return
|
||||
}
|
||||
_, err = client.APINodeRPC().FindCurrentAPINodeVersion(client.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
_ = client.Close()
|
||||
this.Fail("无法连接到API节点地址'" + endpoint + "':" + err.Error())
|
||||
actionutils.Fail(this, err)
|
||||
return
|
||||
}
|
||||
_ = client.Close()
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ func (this *MySQLInstaller) InstallFromFile(xzFilePath string, targetDir string)
|
||||
} else { // yum
|
||||
yumExe, err := exec.LookPath("yum")
|
||||
if err == nil && len(yumExe) > 0 {
|
||||
for _, lib := range []string{"libaio", "ncurses-libs", "ncurses-compat-libs"} {
|
||||
for _, lib := range []string{"libaio", "ncurses-libs", "ncurses-compat-libs", "numactl-libs"} {
|
||||
var cmd = utils.NewCmd("yum", "-y", "install", lib)
|
||||
_ = cmd.Run()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
@@ -34,7 +34,7 @@ func FindAllMenuMaps(nodeLogsType string, countUnreadNodeLogs int64, countUnread
|
||||
"code": "cert",
|
||||
},
|
||||
{
|
||||
"name": "服务分组",
|
||||
"name": "网站分组",
|
||||
"url": "/servers/groups",
|
||||
"code": "group",
|
||||
},
|
||||
@@ -90,34 +90,44 @@ func FindAllMenuMaps(nodeLogsType string, countUnreadNodeLogs int64, countUnread
|
||||
"code": "clusters",
|
||||
"module": configloaders.AdminModuleCodeNode,
|
||||
"name": "边缘节点",
|
||||
"subtitle": "集群列表",
|
||||
"subtitle": "",
|
||||
"icon": "cloud",
|
||||
"subItems": []maps.Map{
|
||||
{
|
||||
"name": "运行日志",
|
||||
"name": "集群列表",
|
||||
"url": "/clusters",
|
||||
"code": "cluster",
|
||||
},
|
||||
{
|
||||
"name": "节点日志",
|
||||
"url": "/clusters/logs?type=" + nodeLogsType,
|
||||
"code": "log",
|
||||
"badge": countUnreadNodeLogs,
|
||||
},
|
||||
{
|
||||
"name": "SSH认证",
|
||||
"url": "/clusters/grants",
|
||||
"code": "grant",
|
||||
},
|
||||
{
|
||||
"name": "区域设置",
|
||||
"url": "/clusters/regions",
|
||||
"code": "region",
|
||||
},
|
||||
{
|
||||
"name": "节点SSH",
|
||||
"url": "/clusters/grants",
|
||||
"code": "grant",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"code": "dns",
|
||||
"module": configloaders.AdminModuleCodeDNS,
|
||||
"name": "域名解析",
|
||||
"subtitle": "集群列表",
|
||||
"subtitle": "",
|
||||
"icon": "globe",
|
||||
"subItems": []maps.Map{
|
||||
{
|
||||
"name": "集群列表",
|
||||
"url": "/dns",
|
||||
"code": "cluster",
|
||||
},
|
||||
{
|
||||
"name": "DNS服务商",
|
||||
"url": "/dns/providers",
|
||||
@@ -140,7 +150,7 @@ func FindAllMenuMaps(nodeLogsType string, countUnreadNodeLogs int64, countUnread
|
||||
"code": "admins",
|
||||
"module": configloaders.AdminModuleCodeAdmin,
|
||||
"name": "系统用户",
|
||||
"subtitle": "用户列表",
|
||||
"subtitle": "",
|
||||
"icon": "user secret",
|
||||
},
|
||||
{
|
||||
@@ -153,9 +163,14 @@ func FindAllMenuMaps(nodeLogsType string, countUnreadNodeLogs int64, countUnread
|
||||
"code": "settings",
|
||||
"module": configloaders.AdminModuleCodeSetting,
|
||||
"name": "系统设置",
|
||||
"subtitle": "基本设置",
|
||||
"subtitle": "",
|
||||
"icon": "setting",
|
||||
"subItems": []maps.Map{
|
||||
{
|
||||
"name": "基础设置",
|
||||
"url": "/settings",
|
||||
"code": "basic",
|
||||
},
|
||||
{
|
||||
"name": "高级设置",
|
||||
"url": "/settings/advanced",
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/admins"
|
||||
|
||||
// API节点
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/api"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/api/node"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/api"
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/api/node"
|
||||
|
||||
// 节点集群
|
||||
_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters"
|
||||
|
||||
BIN
web/public/audios/alert.ogg
Normal file
BIN
web/public/audios/alert.ogg
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -3894,7 +3894,8 @@ Vue.component("ssl-config-box", {
|
||||
props: [
|
||||
"v-ssl-policy",
|
||||
"v-protocol",
|
||||
"v-server-id"
|
||||
"v-server-id",
|
||||
"v-support-http3"
|
||||
],
|
||||
created: function () {
|
||||
let that = this
|
||||
@@ -3918,6 +3919,7 @@ Vue.component("ssl-config-box", {
|
||||
cipherSuitesIsOn: false,
|
||||
cipherSuites: [],
|
||||
http2Enabled: true,
|
||||
http3Enabled: false,
|
||||
ocspIsOn: false
|
||||
}
|
||||
} else {
|
||||
@@ -4295,6 +4297,15 @@ Vue.component("ssl-config-box", {
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="vProtocol == 'https' && vSupportHttp3">
|
||||
<td class="title">启用HTTP/3</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="policy.http3Enabled"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">选择证书</td>
|
||||
<td>
|
||||
@@ -5049,6 +5060,7 @@ Vue.component("http-cache-ref-box", {
|
||||
simpleCond: null, // 简单条件
|
||||
allowChunkedEncoding: true,
|
||||
allowPartialContent: true,
|
||||
forcePartialContent: false,
|
||||
enableIfNoneMatch: false,
|
||||
enableIfModifiedSince: false,
|
||||
isReverse: this.vIsReverse,
|
||||
@@ -5193,13 +5205,15 @@ Vue.component("http-cache-ref-box", {
|
||||
},
|
||||
template: `<tbody>
|
||||
<tr v-if="condCategory == 'simple'">
|
||||
<td class="title color-border">条件类型 *</td>
|
||||
<td class="title">条件类型 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="condType" v-model="condType" @change="changeCondType(condType, false)">
|
||||
<option value="url-extension">URL扩展名</option>
|
||||
<option value="url-prefix">URL前缀</option>
|
||||
<option value="url-extension">文件扩展名</option>
|
||||
<option value="url-eq-index">首页</option>
|
||||
<option value="url-all">全站</option>
|
||||
<option value="url-prefix">URL前缀</option>
|
||||
<option value="url-eq">URL完整路径</option>
|
||||
<option value="url-wildcard-match">URL通配符</option>
|
||||
<option value="url-regexp">URL正则匹配</option>
|
||||
<option value="params">参数匹配</option>
|
||||
</select>
|
||||
@@ -5207,7 +5221,7 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="condCategory == 'simple'">
|
||||
<td class="color-border">{{condComponent.paramsTitle}} *</td>
|
||||
<td>{{condComponent.paramsTitle}} *</td>
|
||||
<td>
|
||||
<component :is="condComponent.component" :v-cond="ref.simpleCond" v-if="condComponent.type != 'params'"></component>
|
||||
<table class="ui table" v-if="condComponent.type == 'params'">
|
||||
@@ -5216,7 +5230,7 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="condCategory == 'simple' && condComponent.caseInsensitive">
|
||||
<td class="color-border">不区分大小写</td>
|
||||
<td>不区分大小写</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="condIsCaseInsensitive" value="1" v-model="condIsCaseInsensitive"/>
|
||||
@@ -5239,19 +5253,19 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td class="color-border">缓存Key *</td>
|
||||
<td>
|
||||
<input type="text" v-model="ref.key" @input="changeKey(ref.key)"/>
|
||||
<p class="comment">用来区分不同缓存内容的唯一Key。<request-variables-describer ref="variablesDescriber"></request-variables-describer>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td class="color-border">忽略URI参数</td>
|
||||
<td>忽略URI参数</td>
|
||||
<td>
|
||||
<checkbox v-model="keyIgnoreArgs"></checkbox>
|
||||
<p class="comment">选中后,表示缓存Key中不包含URI参数(即问号(?))后面的内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td>缓存Key *</td>
|
||||
<td>
|
||||
<input type="text" v-model="ref.key" @input="changeKey(ref.key)"/>
|
||||
<p class="comment">用来区分不同缓存内容的唯一Key。<request-variables-describer ref="variablesDescriber"></request-variables-describer>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td colspan="2"><more-options-indicator @change="changeOptionsVisible"></more-options-indicator></td>
|
||||
</tr>
|
||||
@@ -5296,6 +5310,13 @@ Vue.component("http-cache-ref-box", {
|
||||
<p class="comment">选中后,支持缓存源站返回的某个区间的内容,该内容通过<code-label>206 Partial Content</code-label>状态码返回。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse && ref.allowPartialContent">
|
||||
<td>强制返回区间内容</td>
|
||||
<td>
|
||||
<checkbox name="forcePartialContent" value="1" v-model="ref.forcePartialContent"></checkbox>
|
||||
<p class="comment">选中后,表示无论客户端是否发送<code-label>Range</code-label>报头,都会优先尝试返回已缓存的区间内容;如果你的应用有不支持区间内容的客户端(比如有些下载软件不支持<code-label>206 Partial Content</code-label>),请务必关闭此功能。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
<td>状态码列表</td>
|
||||
<td>
|
||||
@@ -6254,8 +6275,8 @@ Vue.component("http-cache-config-box", {
|
||||
</div>
|
||||
|
||||
<div v-show="isOn()" style="margin-top: 1em">
|
||||
<h4>缓存条件</h4>
|
||||
<http-cache-refs-config-box :v-cache-config="cacheConfig" :v-cache-refs="cacheConfig.cacheRefs" :v-web-id="vWebId"></http-cache-refs-config-box>
|
||||
<h4>缓存条件 <a href="" style="font-size: 0.8em" @click.prevent="$refs.cacheRefsConfigBoxRef.addRef(false)">[添加]</a> </h4>
|
||||
<http-cache-refs-config-box ref="cacheRefsConfigBoxRef" :v-cache-config="cacheConfig" :v-cache-refs="cacheConfig.cacheRefs" :v-web-id="vWebId"></http-cache-refs-config-box>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
@@ -6967,7 +6988,7 @@ Vue.component("http-cache-refs-config-box", {
|
||||
</table>
|
||||
<p class="comment" v-if="refs.length > 1">所有条件匹配顺序为从上到下,可以拖动左侧的<i class="icon bars"></i>排序。服务设置的优先级比全局缓存策略设置的优先级要高。</p>
|
||||
|
||||
<button class="ui button tiny" @click.prevent="addRef(false)" type="button">+添加缓存条件</button> <a href="" @click.prevent="addRef(true)">+添加不缓存条件</a>
|
||||
<button class="ui button tiny" @click.prevent="addRef(false)" type="button">+添加缓存条件</button> <a href="" @click.prevent="addRef(true)" style="font-size: 0.8em">+添加不缓存条件</a>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
@@ -7074,28 +7095,31 @@ Vue.component("origin-list-table", {
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="origin in vOrigins">
|
||||
<td :class="{disabled:!origin.isOn}">
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)" :class="{disabled:!origin.isOn}">{{origin.addr}} <i class="icon expand small"></i></a>
|
||||
<div style="margin-top: 0.3em">
|
||||
<tiny-basic-label v-if="origin.name.length > 0">{{origin.name}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.hasCert">证书</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.host != null && origin.host.length > 0">主机名: {{origin.host}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.followPort">端口跟随</tiny-basic-label>
|
||||
|
||||
<span v-if="origin.domains != null && origin.domains.length > 0"><tiny-basic-label v-for="domain in origin.domains">匹配: {{domain}}</tiny-basic-label></span>
|
||||
<span v-else-if="hasMatchedDomains"><tiny-basic-label>匹配: 所有域名</tiny-basic-label></span>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!origin.isOn}">{{origin.weight}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="origin.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteOrigin(origin.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr v-for="origin in vOrigins">
|
||||
<td :class="{disabled:!origin.isOn}">
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)" :class="{disabled:!origin.isOn}">{{origin.addr}} <i class="icon expand small"></i></a>
|
||||
<div style="margin-top: 0.3em">
|
||||
<tiny-basic-label v-if="origin.isOSS"><i class="icon hdd outline"></i>对象存储</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.name.length > 0">{{origin.name}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.hasCert">证书</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.host != null && origin.host.length > 0">主机名: {{origin.host}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.followPort">端口跟随</tiny-basic-label>
|
||||
|
||||
<span v-if="origin.domains != null && origin.domains.length > 0"><tiny-basic-label v-for="domain in origin.domains">匹配: {{domain}}</tiny-basic-label></span>
|
||||
<span v-else-if="hasMatchedDomains"><tiny-basic-label>匹配: 所有域名</tiny-basic-label></span>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!origin.isOn}">{{origin.weight}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="origin.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteOrigin(origin.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`
|
||||
})
|
||||
|
||||
@@ -7392,13 +7416,13 @@ Vue.component("http-websocket-box", {
|
||||
<more-options-tbody @change="changeAdvancedVisible" v-show="isOn()"></more-options-tbody>
|
||||
<tbody v-show="isOn() && advancedVisible">
|
||||
<tr>
|
||||
<td class="color-border">是否传递请求来源域</td>
|
||||
<td class="color-border">传递请求来源域</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" v-model="websocketConfig.requestSameOrigin"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中表示把接收到的请求中的<span class="ui label tiny">Origin</span>字段传递到源站。</p>
|
||||
<p class="comment">选中后,表示把接收到的请求中的<code-label>Origin</code-label>字段传递到源站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -9615,7 +9639,7 @@ Vue.component("http-header-policy-box", {
|
||||
<div v-if="((!vIsLocation && !vIsGroup) || requestHeaderRef.isPrior) && type == 'request'">
|
||||
<div v-if="vHasGroupRequestConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#request'">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#request'">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
<div :class="{'opacity-mask': vHasGroupRequestConfig}">
|
||||
<h4>设置请求Header <a href="" @click.prevent="addSettingHeader(vRequestHeaderPolicy.id)" style="font-size: 0.8em">[添加新Header]</a></h4>
|
||||
@@ -9688,7 +9712,7 @@ Vue.component("http-header-policy-box", {
|
||||
<div v-if="((!vIsLocation && !vIsGroup) || responseHeaderRef.isPrior) && type == 'response'">
|
||||
<div v-if="vHasGroupResponseConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#response'">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#response'">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
<div :class="{'opacity-mask': vHasGroupResponseConfig}">
|
||||
<h4>设置响应Header <a href="" @click.prevent="addSettingHeader(vResponseHeaderPolicy.id)" style="font-size: 0.8em">[添加新Header]</a></h4>
|
||||
@@ -12843,6 +12867,64 @@ Vue.component("http-firewall-block-options", {
|
||||
`
|
||||
})
|
||||
|
||||
Vue.component("http-oss-bucket-params", {
|
||||
props: ["v-oss-config", "v-params", "name"],
|
||||
data: function () {
|
||||
let params = this.vParams
|
||||
if (params == null) {
|
||||
params = []
|
||||
}
|
||||
|
||||
let ossConfig = this.vOssConfig
|
||||
if (ossConfig == null) {
|
||||
ossConfig = {
|
||||
bucketParam: "input",
|
||||
bucketName: "",
|
||||
bucketArgName: ""
|
||||
}
|
||||
} else {
|
||||
// 兼容以往
|
||||
if (ossConfig.bucketParam != null && ossConfig.bucketParam.length == 0) {
|
||||
ossConfig.bucketParam = "input"
|
||||
}
|
||||
if (ossConfig.options != null && ossConfig.options.bucketName != null && ossConfig.options.bucketName.length > 0) {
|
||||
ossConfig.bucketName = ossConfig.options.bucketName
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
params: params,
|
||||
ossConfig: ossConfig
|
||||
}
|
||||
},
|
||||
template: `<tbody>
|
||||
<tr>
|
||||
<td>{{name}}名称获取方式 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="bucketParam" v-model="ossConfig.bucketParam">
|
||||
<option v-for="param in params" :value="param.code" v-if="param.example.length == 0">{{param.name.replace("\${optionName}", name)}}</option>
|
||||
<option v-for="param in params" :value="param.code" v-if="param.example.length > 0">{{param.name}} - {{param.example}}</option>
|
||||
</select>
|
||||
<p class="comment" v-for="param in params" v-if="param.code == ossConfig.bucketParam">{{param.description.replace("\${optionName}", name)}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="ossConfig.bucketParam == 'input'">
|
||||
<td>{{name}}名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="bucketName" maxlength="100" v-model="ossConfig.bucketName"/>
|
||||
<p class="comment">{{name}}名称,类似于<code-label>bucket-12345678</code-label>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="ossConfig.bucketParam == 'arg'">
|
||||
<td>{{name}}参数名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="bucketArgName" maxlength="100" v-model="ossConfig.bucketArgName"/>
|
||||
<p class="comment">{{name}}参数名称,比如<code-label>?myBucketName=BUCKET-NAME</code-label>中的<code-label>myBucketName</code-label>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>`
|
||||
})
|
||||
|
||||
Vue.component("http-request-cond-view", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
@@ -13467,6 +13549,36 @@ Vue.component("http-cond-url-eq-index", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 全站
|
||||
Vue.component("http-cond-url-all", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
let cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "prefix",
|
||||
value: "/",
|
||||
isCaseInsensitive: false
|
||||
}
|
||||
if (this.vCond != null && typeof this.vCond.value == "string") {
|
||||
cond.value = this.vCond.value
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeCaseInsensitive: function (isCaseInsensitive) {
|
||||
this.cond.isCaseInsensitive = isCaseInsensitive
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value" disabled="disabled" style="background: #eee"/>
|
||||
<p class="comment">支持全站所有URL。</p>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL精准匹配
|
||||
Vue.component("http-cond-url-eq", {
|
||||
props: ["v-cond"],
|
||||
@@ -13599,6 +13711,38 @@ Vue.component("http-cond-url-not-regexp", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL通配符
|
||||
Vue.component("http-cond-url-wildcard-match", {
|
||||
props: ["v-cond"],
|
||||
mounted: function () {
|
||||
this.$refs.valueInput.focus()
|
||||
},
|
||||
data: function () {
|
||||
let cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "wildcard match",
|
||||
value: "",
|
||||
isCaseInsensitive: false
|
||||
}
|
||||
if (this.vCond != null && typeof this.vCond.value == "string") {
|
||||
cond.value = this.vCond.value
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeCaseInsensitive: function (isCaseInsensitive) {
|
||||
this.cond.isCaseInsensitive = isCaseInsensitive
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value" ref="valueInput"/>
|
||||
<p class="comment">匹配URL的通配符,用星号(<code-label>*</code-label>)表示任意字符,比如(<code-label>/images/*.png</code-label>、<code-label>/static/*</code-label>,不需要带域名。</p>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// User-Agent正则匹配
|
||||
Vue.component("http-cond-user-agent-regexp", {
|
||||
@@ -15335,7 +15479,7 @@ Vue.component("api-node-addresses-box", {
|
||||
// 添加IP地址
|
||||
addAddr: function () {
|
||||
let that = this;
|
||||
teaweb.popup("/api/node/createAddrPopup", {
|
||||
teaweb.popup("/settings/api/node/createAddrPopup", {
|
||||
height: "16em",
|
||||
callback: function (resp) {
|
||||
that.addrs.push(resp.data.addr);
|
||||
@@ -15347,7 +15491,7 @@ Vue.component("api-node-addresses-box", {
|
||||
updateAddr: function (index, addr) {
|
||||
let that = this;
|
||||
window.UPDATING_ADDR = addr
|
||||
teaweb.popup("/api/node/updateAddrPopup?addressId=", {
|
||||
teaweb.popup("/settings/api/node/updateAddrPopup?addressId=", {
|
||||
callback: function (resp) {
|
||||
Vue.set(that.addrs, index, resp.data.addr);
|
||||
}
|
||||
@@ -18615,7 +18759,8 @@ Vue.component("node-schedule-conds-box", {
|
||||
},
|
||||
valueCPU: 80,
|
||||
valueMemory: 90,
|
||||
valueLoad: 20
|
||||
valueLoad: 20,
|
||||
valueRate: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -18721,6 +18866,16 @@ Vue.component("node-schedule-conds-box", {
|
||||
}
|
||||
value = load
|
||||
break
|
||||
case "rate":
|
||||
let rate = parseInt(this.valueRate.toString())
|
||||
if (isNaN(rate)) {
|
||||
rate = 0
|
||||
}
|
||||
if (rate < 0) {
|
||||
rate = 0
|
||||
}
|
||||
value = rate
|
||||
break
|
||||
}
|
||||
|
||||
this.condsConfig.conds.push({
|
||||
@@ -18759,6 +18914,9 @@ Vue.component("node-schedule-conds-box", {
|
||||
case "load":
|
||||
cond.valueFormat = cond.value
|
||||
return
|
||||
case "rate":
|
||||
cond.valueFormat = cond.value + "/秒"
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -18855,7 +19013,15 @@ Vue.component("node-schedule-conds-box", {
|
||||
|
||||
<!-- load -->
|
||||
<div v-if="param.valueType == 'load'">
|
||||
<input type="text" v-model="valueLoad" maxlength="6" size="6" style="width: 6em" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<input type="text" v-model="valueLoad" maxlength="3" size="3" style="width: 4em" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
</div>
|
||||
|
||||
<!-- rate -->
|
||||
<div v-if="param.valueType == 'rate'">
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" v-model="valueRate" maxlength="8" size="8" style="width: 8em" @keyup.enter="confirm" @keypress.enter.prevent="1"/>
|
||||
<span class="ui label">/秒</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -19756,6 +19922,9 @@ Vue.component("node-schedule-conds-viewer", {
|
||||
case "load":
|
||||
cond.valueFormat = cond.value
|
||||
return
|
||||
case "rate":
|
||||
cond.valueFormat = cond.value + "/秒"
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -20013,6 +20182,90 @@ Vue.component("dns-resolver-config-box", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("dns-resolvers-config-box", {
|
||||
props: ["value", "name"],
|
||||
data: function () {
|
||||
let resolvers = this.value
|
||||
if (resolvers == null) {
|
||||
resolvers = []
|
||||
}
|
||||
|
||||
let name = this.name
|
||||
if (name == null || name.length == 0) {
|
||||
name = "dnsResolversJSON"
|
||||
}
|
||||
|
||||
return {
|
||||
formName: name,
|
||||
resolvers: resolvers,
|
||||
|
||||
host: "",
|
||||
|
||||
isAdding: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add: function () {
|
||||
this.isAdding = true
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.hostRef.focus()
|
||||
})
|
||||
},
|
||||
confirm: function () {
|
||||
let host = this.host.trim()
|
||||
if (host.length == 0) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.hostRef.focus()
|
||||
})
|
||||
return
|
||||
}
|
||||
this.resolvers.push({
|
||||
host: host,
|
||||
port: 0, // TODO
|
||||
protocol: "" // TODO
|
||||
})
|
||||
this.cancel()
|
||||
},
|
||||
cancel: function () {
|
||||
this.isAdding = false
|
||||
this.host = ""
|
||||
this.port = 0
|
||||
this.protocol = ""
|
||||
},
|
||||
remove: function (index) {
|
||||
this.resolvers.$remove(index)
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" :name="formName" :value="JSON.stringify(resolvers)"/>
|
||||
<div v-if="resolvers.length > 0">
|
||||
<div v-for="(resolver, index) in resolvers" class="ui label basic small">
|
||||
<span v-if="resolver.protocol.length > 0">{{resolver.protocol}}</span>{{resolver.host}}<span v-if="resolver.port > 0">:{{resolver.port}}</span>
|
||||
|
||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isAdding" style="margin-top: 1em">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="x.x.x.x" @keyup.enter="confirm" @keypress.enter.prevent="1" ref="hostRef" v-model="host"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button tiny" type="button" @click.prevent="confirm">确认</button>
|
||||
<a href="" @click.prevent="cancel" title="取消"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!isAdding" style="margin-top: 1em">
|
||||
<button class="ui button tiny" type="button" @click.prevent="add">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
|
||||
Vue.component("ad-instance-objects-box", {
|
||||
props: ["v-objects", "v-user-id"],
|
||||
mounted: function () {
|
||||
@@ -20266,9 +20519,9 @@ Vue.component("grant-selector", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
window.REQUEST_COND_COMPONENTS = [{"type":"url-extension","name":"URL扩展名","description":"根据URL中的文件路径扩展名进行过滤","component":"http-cond-url-extension","paramsTitle":"扩展名列表","isRequest":true,"caseInsensitive":false},{"type":"url-eq-index","name":"首页","description":"检查URL路径是为\"/\"","component":"http-cond-url-eq-index","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":false},{"type":"url-prefix","name":"URL前缀","description":"根据URL中的文件路径前缀进行过滤","component":"http-cond-url-prefix","paramsTitle":"URL前缀","isRequest":true,"caseInsensitive":true},{"type":"url-eq","name":"URL完整路径","description":"检查URL中的文件路径是否一致","component":"http-cond-url-eq","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":true},{"type":"url-regexp","name":"URL正则匹配","description":"使用正则表达式检查URL中的文件路径是否一致","component":"http-cond-url-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"user-agent-regexp","name":"User-Agent正则匹配","description":"使用正则表达式检查User-Agent中是否含有某些浏览器和系统标识","component":"http-cond-user-agent-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"params","name":"参数匹配","description":"根据参数值进行匹配","component":"http-cond-params","paramsTitle":"参数配置","isRequest":true,"caseInsensitive":false},{"type":"url-not-extension","name":"排除:URL扩展名","description":"根据URL中的文件路径扩展名进行过滤","component":"http-cond-url-not-extension","paramsTitle":"扩展名列表","isRequest":true,"caseInsensitive":false},{"type":"url-not-prefix","name":"排除:URL前缀","description":"根据URL中的文件路径前缀进行过滤","component":"http-cond-url-not-prefix","paramsTitle":"URL前缀","isRequest":true,"caseInsensitive":true},{"type":"url-not-eq","name":"排除:URL完整路径","description":"检查URL中的文件路径是否一致","component":"http-cond-url-not-eq","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":true},{"type":"url-not-regexp","name":"排除:URL正则匹配","description":"使用正则表达式检查URL中的文件路径是否一致,如果一致,则不匹配","component":"http-cond-url-not-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"user-agent-not-regexp","name":"排除:User-Agent正则匹配","description":"使用正则表达式检查User-Agent中是否含有某些浏览器和系统标识,如果含有,则不匹配","component":"http-cond-user-agent-not-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"mime-type","name":"内容MimeType","description":"根据服务器返回的内容的MimeType进行过滤。注意:当用于缓存条件时,此条件需要结合别的请求条件使用。","component":"http-cond-mime-type","paramsTitle":"MimeType列表","isRequest":false,"caseInsensitive":false}]
|
||||
window.REQUEST_COND_COMPONENTS = [{"type":"url-extension","name":"文件扩展名","description":"根据URL中的文件路径扩展名进行过滤","component":"http-cond-url-extension","paramsTitle":"扩展名列表","isRequest":true,"caseInsensitive":false},{"type":"url-eq-index","name":"首页","description":"检查URL路径是为\"/\"","component":"http-cond-url-eq-index","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":false},{"type":"url-all","name":"全站","description":"全站所有URL","component":"http-cond-url-all","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":false},{"type":"url-prefix","name":"URL前缀","description":"根据URL中的文件路径前缀进行过滤","component":"http-cond-url-prefix","paramsTitle":"URL前缀","isRequest":true,"caseInsensitive":true},{"type":"url-eq","name":"URL完整路径","description":"检查URL中的文件路径是否一致","component":"http-cond-url-eq","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":true},{"type":"url-regexp","name":"URL正则匹配","description":"使用正则表达式检查URL中的文件路径是否一致","component":"http-cond-url-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"url-wildcard-match","name":"URL通配符","description":"使用通配符检查URL中的文件路径是否一致","component":"http-cond-url-wildcard-match","paramsTitle":"通配符","isRequest":true,"caseInsensitive":true},{"type":"user-agent-regexp","name":"User-Agent正则匹配","description":"使用正则表达式检查User-Agent中是否含有某些浏览器和系统标识","component":"http-cond-user-agent-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"params","name":"参数匹配","description":"根据参数值进行匹配","component":"http-cond-params","paramsTitle":"参数配置","isRequest":true,"caseInsensitive":false},{"type":"url-not-extension","name":"排除:URL扩展名","description":"根据URL中的文件路径扩展名进行过滤","component":"http-cond-url-not-extension","paramsTitle":"扩展名列表","isRequest":true,"caseInsensitive":false},{"type":"url-not-prefix","name":"排除:URL前缀","description":"根据URL中的文件路径前缀进行过滤","component":"http-cond-url-not-prefix","paramsTitle":"URL前缀","isRequest":true,"caseInsensitive":true},{"type":"url-not-eq","name":"排除:URL完整路径","description":"检查URL中的文件路径是否一致","component":"http-cond-url-not-eq","paramsTitle":"URL完整路径","isRequest":true,"caseInsensitive":true},{"type":"url-not-regexp","name":"排除:URL正则匹配","description":"使用正则表达式检查URL中的文件路径是否一致,如果一致,则不匹配","component":"http-cond-url-not-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"user-agent-not-regexp","name":"排除:User-Agent正则匹配","description":"使用正则表达式检查User-Agent中是否含有某些浏览器和系统标识,如果含有,则不匹配","component":"http-cond-user-agent-not-regexp","paramsTitle":"正则表达式","isRequest":true,"caseInsensitive":true},{"type":"mime-type","name":"内容MimeType","description":"根据服务器返回的内容的MimeType进行过滤。注意:当用于缓存条件时,此条件需要结合别的请求条件使用。","component":"http-cond-mime-type","paramsTitle":"MimeType列表","isRequest":false,"caseInsensitive":false}]
|
||||
|
||||
window.REQUEST_COND_OPERATORS = [{"description":"判断是否正则表达式匹配","name":"正则表达式匹配","op":"regexp"},{"description":"判断是否正则表达式不匹配","name":"正则表达式不匹配","op":"not regexp"},{"description":"使用字符串对比参数值是否相等于某个值","name":"字符串等于","op":"eq"},{"description":"参数值包含某个前缀","name":"字符串前缀","op":"prefix"},{"description":"参数值包含某个后缀","name":"字符串后缀","op":"suffix"},{"description":"参数值包含另外一个字符串","name":"字符串包含","op":"contains"},{"description":"参数值不包含另外一个字符串","name":"字符串不包含","op":"not contains"},{"description":"使用字符串对比参数值是否不相等于某个值","name":"字符串不等于","op":"not"},{"description":"判断参数值在某个列表中","name":"在列表中","op":"in"},{"description":"判断参数值不在某个列表中","name":"不在列表中","op":"not in"},{"description":"判断小写的扩展名(不带点)在某个列表中","name":"扩展名","op":"file ext"},{"description":"判断MimeType在某个列表中,支持类似于image/*的语法","name":"MimeType","op":"mime type"},{"description":"判断版本号在某个范围内,格式为version1,version2","name":"版本号范围","op":"version range"},{"description":"将参数转换为整数数字后进行对比","name":"整数等于","op":"eq int"},{"description":"将参数转换为可以有小数的浮点数字进行对比","name":"浮点数等于","op":"eq float"},{"description":"将参数转换为数字进行对比","name":"数字大于","op":"gt"},{"description":"将参数转换为数字进行对比","name":"数字大于等于","op":"gte"},{"description":"将参数转换为数字进行对比","name":"数字小于","op":"lt"},{"description":"将参数转换为数字进行对比","name":"数字小于等于","op":"lte"},{"description":"对整数参数值取模,除数为10,对比值为余数","name":"整数取模10","op":"mod 10"},{"description":"对整数参数值取模,除数为100,对比值为余数","name":"整数取模100","op":"mod 100"},{"description":"对整数参数值取模,对比值格式为:除数,余数,比如10,1","name":"整数取模","op":"mod"},{"description":"将参数转换为IP进行对比","name":"IP等于","op":"eq ip"},{"description":"将参数转换为IP进行对比","name":"IP大于","op":"gt ip"},{"description":"将参数转换为IP进行对比","name":"IP大于等于","op":"gte ip"},{"description":"将参数转换为IP进行对比","name":"IP小于","op":"lt ip"},{"description":"将参数转换为IP进行对比","name":"IP小于等于","op":"lte ip"},{"description":"IP在某个范围之内,范围格式可以是英文逗号分隔的\u003ccode-label\u003e开始IP,结束IP\u003c/code-label\u003e,比如\u003ccode-label\u003e192.168.1.100,192.168.2.200\u003c/code-label\u003e,或者CIDR格式的ip/bits,比如\u003ccode-label\u003e192.168.2.1/24\u003c/code-label\u003e","name":"IP范围","op":"ip range"},{"description":"对IP参数值取模,除数为10,对比值为余数","name":"IP取模10","op":"ip mod 10"},{"description":"对IP参数值取模,除数为100,对比值为余数","name":"IP取模100","op":"ip mod 100"},{"description":"对IP参数值取模,对比值格式为:除数,余数,比如10,1","name":"IP取模","op":"ip mod"},{"description":"判断参数值解析后的文件是否存在","name":"文件存在","op":"file exist"},{"description":"判断参数值解析后的文件是否不存在","name":"文件不存在","op":"file not exist"}]
|
||||
window.REQUEST_COND_OPERATORS = [{"description":"判断是否正则表达式匹配","name":"正则表达式匹配","op":"regexp"},{"description":"判断是否正则表达式不匹配","name":"正则表达式不匹配","op":"not regexp"},{"description":"判断是否和指定的通配符匹配","name":"通配符匹配","op":"wildcard match"},{"description":"判断是否和指定的通配符不匹配","name":"通配符不匹配","op":"wildcard not match"},{"description":"使用字符串对比参数值是否相等于某个值","name":"字符串等于","op":"eq"},{"description":"参数值包含某个前缀","name":"字符串前缀","op":"prefix"},{"description":"参数值包含某个后缀","name":"字符串后缀","op":"suffix"},{"description":"参数值包含另外一个字符串","name":"字符串包含","op":"contains"},{"description":"参数值不包含另外一个字符串","name":"字符串不包含","op":"not contains"},{"description":"使用字符串对比参数值是否不相等于某个值","name":"字符串不等于","op":"not"},{"description":"判断参数值在某个列表中","name":"在列表中","op":"in"},{"description":"判断参数值不在某个列表中","name":"不在列表中","op":"not in"},{"description":"判断小写的扩展名(不带点)在某个列表中","name":"扩展名","op":"file ext"},{"description":"判断MimeType在某个列表中,支持类似于image/*的语法","name":"MimeType","op":"mime type"},{"description":"判断版本号在某个范围内,格式为version1,version2","name":"版本号范围","op":"version range"},{"description":"将参数转换为整数数字后进行对比","name":"整数等于","op":"eq int"},{"description":"将参数转换为可以有小数的浮点数字进行对比","name":"浮点数等于","op":"eq float"},{"description":"将参数转换为数字进行对比","name":"数字大于","op":"gt"},{"description":"将参数转换为数字进行对比","name":"数字大于等于","op":"gte"},{"description":"将参数转换为数字进行对比","name":"数字小于","op":"lt"},{"description":"将参数转换为数字进行对比","name":"数字小于等于","op":"lte"},{"description":"对整数参数值取模,除数为10,对比值为余数","name":"整数取模10","op":"mod 10"},{"description":"对整数参数值取模,除数为100,对比值为余数","name":"整数取模100","op":"mod 100"},{"description":"对整数参数值取模,对比值格式为:除数,余数,比如10,1","name":"整数取模","op":"mod"},{"description":"将参数转换为IP进行对比","name":"IP等于","op":"eq ip"},{"description":"将参数转换为IP进行对比","name":"IP大于","op":"gt ip"},{"description":"将参数转换为IP进行对比","name":"IP大于等于","op":"gte ip"},{"description":"将参数转换为IP进行对比","name":"IP小于","op":"lt ip"},{"description":"将参数转换为IP进行对比","name":"IP小于等于","op":"lte ip"},{"description":"IP在某个范围之内,范围格式可以是英文逗号分隔的\u003ccode-label\u003e开始IP,结束IP\u003c/code-label\u003e,比如\u003ccode-label\u003e192.168.1.100,192.168.2.200\u003c/code-label\u003e,或者CIDR格式的ip/bits,比如\u003ccode-label\u003e192.168.2.1/24\u003c/code-label\u003e","name":"IP范围","op":"ip range"},{"description":"对IP参数值取模,除数为10,对比值为余数","name":"IP取模10","op":"ip mod 10"},{"description":"对IP参数值取模,除数为100,对比值为余数","name":"IP取模100","op":"ip mod 100"},{"description":"对IP参数值取模,对比值格式为:除数,余数,比如10,1","name":"IP取模","op":"ip mod"}]
|
||||
|
||||
window.REQUEST_VARIABLES = [{"code":"${edgeVersion}","description":"","name":"边缘节点版本"},{"code":"${remoteAddr}","description":"会依次根据X-Forwarded-For、X-Real-IP、RemoteAddr获取,适合前端有别的反向代理服务时使用,存在伪造的风险","name":"客户端地址(IP)"},{"code":"${rawRemoteAddr}","description":"返回直接连接服务的客户端原始IP地址","name":"客户端地址(IP)"},{"code":"${remotePort}","description":"","name":"客户端端口"},{"code":"${remoteUser}","description":"","name":"客户端用户名"},{"code":"${requestURI}","description":"比如/hello?name=lily","name":"请求URI"},{"code":"${requestPath}","description":"比如/hello","name":"请求路径(不包括参数)"},{"code":"${requestURL}","description":"比如https://example.com/hello?name=lily","name":"完整的请求URL"},{"code":"${requestLength}","description":"","name":"请求内容长度"},{"code":"${requestMethod}","description":"比如GET、POST","name":"请求方法"},{"code":"${requestFilename}","description":"","name":"请求文件路径"},{"code":"${requestPathExtension}","description":"请求路径中的文件扩展名,包括点符号,比如.html、.png","name":"请求文件扩展名"},{"code":"${requestPathLowerExtension}","description":"请求路径中的文件扩展名,其中大写字母会被自动转换为小写,包括点符号,比如.html、.png","name":"请求文件小写扩展名"},{"code":"${scheme}","description":"","name":"请求协议,http或https"},{"code":"${proto}","description:":"类似于HTTP/1.0","name":"包含版本的HTTP请求协议"},{"code":"${timeISO8601}","description":"比如2018-07-16T23:52:24.839+08:00","name":"ISO 8601格式的时间"},{"code":"${timeLocal}","description":"比如17/Jul/2018:09:52:24 +0800","name":"本地时间"},{"code":"${msec}","description":"比如1531756823.054","name":"带有毫秒的时间"},{"code":"${timestamp}","description":"","name":"unix时间戳,单位为秒"},{"code":"${host}","description":"","name":"主机名"},{"code":"${cname}","description":"比如38b48e4f.goedge.cn","name":"当前网站的CNAME"},{"code":"${serverName}","description":"","name":"接收请求的服务器名"},{"code":"${serverPort}","description":"","name":"接收请求的服务器端口"},{"code":"${referer}","description":"","name":"请求来源URL"},{"code":"${referer.host}","description":"","name":"请求来源URL域名"},{"code":"${userAgent}","description":"","name":"客户端信息"},{"code":"${contentType}","description":"","name":"请求头部的Content-Type"},{"code":"${cookies}","description":"","name":"所有cookie组合字符串"},{"code":"${cookie.NAME}","description":"","name":"单个cookie值"},{"code":"${isArgs}","description":"如果URL有参数,则值为`?`;否则,则值为空","name":"问号(?)标记"},{"code":"${args}","description":"","name":"所有参数组合字符串"},{"code":"${arg.NAME}","description":"","name":"单个参数值"},{"code":"${headers}","description":"","name":"所有Header信息组合字符串"},{"code":"${header.NAME}","description":"","name":"单个Header值"},{"code":"${geo.country.name}","description":"","name":"国家/地区名称"},{"code":"${geo.country.id}","description":"","name":"国家/地区ID"},{"code":"${geo.province.name}","description":"目前只包含中国省份","name":"省份名称"},{"code":"${geo.province.id}","description":"目前只包含中国省份","name":"省份ID"},{"code":"${geo.city.name}","description":"目前只包含中国城市","name":"城市名称"},{"code":"${geo.city.id}","description":"目前只包含中国城市","name":"城市名称"},{"code":"${isp.name}","description":"","name":"ISP服务商名称"},{"code":"${isp.id}","description":"","name":"ISP服务商ID"},{"code":"${browser.os.name}","description":"客户端所在操作系统名称","name":"操作系统名称"},{"code":"${browser.os.version}","description":"客户端所在操作系统版本","name":"操作系统版本"},{"code":"${browser.name}","description":"客户端浏览器名称","name":"浏览器名称"},{"code":"${browser.version}","description":"客户端浏览器版本","name":"浏览器版本"},{"code":"${browser.isMobile}","description":"如果客户端是手机,则值为1,否则为0","name":"手机标识"}]
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Vue.component("api-node-addresses-box", {
|
||||
// 添加IP地址
|
||||
addAddr: function () {
|
||||
let that = this;
|
||||
teaweb.popup("/api/node/createAddrPopup", {
|
||||
teaweb.popup("/settings/api/node/createAddrPopup", {
|
||||
height: "16em",
|
||||
callback: function (resp) {
|
||||
that.addrs.push(resp.data.addr);
|
||||
@@ -25,7 +25,7 @@ Vue.component("api-node-addresses-box", {
|
||||
updateAddr: function (index, addr) {
|
||||
let that = this;
|
||||
window.UPDATING_ADDR = addr
|
||||
teaweb.popup("/api/node/updateAddrPopup?addressId=", {
|
||||
teaweb.popup("/settings/api/node/updateAddrPopup?addressId=", {
|
||||
callback: function (resp) {
|
||||
Vue.set(that.addrs, index, resp.data.addr);
|
||||
}
|
||||
|
||||
83
web/public/js/components/dns/dns-resolvers-config-box.js
Normal file
83
web/public/js/components/dns/dns-resolvers-config-box.js
Normal file
@@ -0,0 +1,83 @@
|
||||
Vue.component("dns-resolvers-config-box", {
|
||||
props: ["value", "name"],
|
||||
data: function () {
|
||||
let resolvers = this.value
|
||||
if (resolvers == null) {
|
||||
resolvers = []
|
||||
}
|
||||
|
||||
let name = this.name
|
||||
if (name == null || name.length == 0) {
|
||||
name = "dnsResolversJSON"
|
||||
}
|
||||
|
||||
return {
|
||||
formName: name,
|
||||
resolvers: resolvers,
|
||||
|
||||
host: "",
|
||||
|
||||
isAdding: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add: function () {
|
||||
this.isAdding = true
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.hostRef.focus()
|
||||
})
|
||||
},
|
||||
confirm: function () {
|
||||
let host = this.host.trim()
|
||||
if (host.length == 0) {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.hostRef.focus()
|
||||
})
|
||||
return
|
||||
}
|
||||
this.resolvers.push({
|
||||
host: host,
|
||||
port: 0, // TODO
|
||||
protocol: "" // TODO
|
||||
})
|
||||
this.cancel()
|
||||
},
|
||||
cancel: function () {
|
||||
this.isAdding = false
|
||||
this.host = ""
|
||||
this.port = 0
|
||||
this.protocol = ""
|
||||
},
|
||||
remove: function (index) {
|
||||
this.resolvers.$remove(index)
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" :name="formName" :value="JSON.stringify(resolvers)"/>
|
||||
<div v-if="resolvers.length > 0">
|
||||
<div v-for="(resolver, index) in resolvers" class="ui label basic small">
|
||||
<span v-if="resolver.protocol.length > 0">{{resolver.protocol}}</span>{{resolver.host}}<span v-if="resolver.port > 0">:{{resolver.port}}</span>
|
||||
|
||||
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isAdding" style="margin-top: 1em">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" placeholder="x.x.x.x" @keyup.enter="confirm" @keypress.enter.prevent="1" ref="hostRef" v-model="host"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button tiny" type="button" @click.prevent="confirm">确认</button>
|
||||
<a href="" @click.prevent="cancel" title="取消"><i class="icon remove small"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!isAdding" style="margin-top: 1em">
|
||||
<button class="ui button tiny" type="button" @click.prevent="add">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
@@ -141,8 +141,8 @@ Vue.component("http-cache-config-box", {
|
||||
</div>
|
||||
|
||||
<div v-show="isOn()" style="margin-top: 1em">
|
||||
<h4>缓存条件</h4>
|
||||
<http-cache-refs-config-box :v-cache-config="cacheConfig" :v-cache-refs="cacheConfig.cacheRefs" :v-web-id="vWebId"></http-cache-refs-config-box>
|
||||
<h4>缓存条件 <a href="" style="font-size: 0.8em" @click.prevent="$refs.cacheRefsConfigBoxRef.addRef(false)">[添加]</a> </h4>
|
||||
<http-cache-refs-config-box ref="cacheRefsConfigBoxRef" :v-cache-config="cacheConfig" :v-cache-refs="cacheConfig.cacheRefs" :v-web-id="vWebId"></http-cache-refs-config-box>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
|
||||
@@ -30,6 +30,7 @@ Vue.component("http-cache-ref-box", {
|
||||
simpleCond: null, // 简单条件
|
||||
allowChunkedEncoding: true,
|
||||
allowPartialContent: true,
|
||||
forcePartialContent: false,
|
||||
enableIfNoneMatch: false,
|
||||
enableIfModifiedSince: false,
|
||||
isReverse: this.vIsReverse,
|
||||
@@ -174,13 +175,15 @@ Vue.component("http-cache-ref-box", {
|
||||
},
|
||||
template: `<tbody>
|
||||
<tr v-if="condCategory == 'simple'">
|
||||
<td class="title color-border">条件类型 *</td>
|
||||
<td class="title">条件类型 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown auto-width" name="condType" v-model="condType" @change="changeCondType(condType, false)">
|
||||
<option value="url-extension">URL扩展名</option>
|
||||
<option value="url-prefix">URL前缀</option>
|
||||
<option value="url-extension">文件扩展名</option>
|
||||
<option value="url-eq-index">首页</option>
|
||||
<option value="url-all">全站</option>
|
||||
<option value="url-prefix">URL前缀</option>
|
||||
<option value="url-eq">URL完整路径</option>
|
||||
<option value="url-wildcard-match">URL通配符</option>
|
||||
<option value="url-regexp">URL正则匹配</option>
|
||||
<option value="params">参数匹配</option>
|
||||
</select>
|
||||
@@ -188,7 +191,7 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="condCategory == 'simple'">
|
||||
<td class="color-border">{{condComponent.paramsTitle}} *</td>
|
||||
<td>{{condComponent.paramsTitle}} *</td>
|
||||
<td>
|
||||
<component :is="condComponent.component" :v-cond="ref.simpleCond" v-if="condComponent.type != 'params'"></component>
|
||||
<table class="ui table" v-if="condComponent.type == 'params'">
|
||||
@@ -197,7 +200,7 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="condCategory == 'simple' && condComponent.caseInsensitive">
|
||||
<td class="color-border">不区分大小写</td>
|
||||
<td>不区分大小写</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="condIsCaseInsensitive" value="1" v-model="condIsCaseInsensitive"/>
|
||||
@@ -220,19 +223,19 @@ Vue.component("http-cache-ref-box", {
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td class="color-border">缓存Key *</td>
|
||||
<td>
|
||||
<input type="text" v-model="ref.key" @input="changeKey(ref.key)"/>
|
||||
<p class="comment">用来区分不同缓存内容的唯一Key。<request-variables-describer ref="variablesDescriber"></request-variables-describer>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td class="color-border">忽略URI参数</td>
|
||||
<td>忽略URI参数</td>
|
||||
<td>
|
||||
<checkbox v-model="keyIgnoreArgs"></checkbox>
|
||||
<p class="comment">选中后,表示缓存Key中不包含URI参数(即问号(?))后面的内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td>缓存Key *</td>
|
||||
<td>
|
||||
<input type="text" v-model="ref.key" @input="changeKey(ref.key)"/>
|
||||
<p class="comment">用来区分不同缓存内容的唯一Key。<request-variables-describer ref="variablesDescriber"></request-variables-describer>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="!vIsReverse">
|
||||
<td colspan="2"><more-options-indicator @change="changeOptionsVisible"></more-options-indicator></td>
|
||||
</tr>
|
||||
@@ -277,6 +280,13 @@ Vue.component("http-cache-ref-box", {
|
||||
<p class="comment">选中后,支持缓存源站返回的某个区间的内容,该内容通过<code-label>206 Partial Content</code-label>状态码返回。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse && ref.allowPartialContent">
|
||||
<td>强制返回区间内容</td>
|
||||
<td>
|
||||
<checkbox name="forcePartialContent" value="1" v-model="ref.forcePartialContent"></checkbox>
|
||||
<p class="comment">选中后,表示无论客户端是否发送<code-label>Range</code-label>报头,都会优先尝试返回已缓存的区间内容;如果你的应用有不支持区间内容的客户端(比如有些下载软件不支持<code-label>206 Partial Content</code-label>),请务必关闭此功能。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="moreOptionsVisible && !vIsReverse">
|
||||
<td>状态码列表</td>
|
||||
<td>
|
||||
|
||||
@@ -214,7 +214,7 @@ Vue.component("http-cache-refs-config-box", {
|
||||
</table>
|
||||
<p class="comment" v-if="refs.length > 1">所有条件匹配顺序为从上到下,可以拖动左侧的<i class="icon bars"></i>排序。服务设置的优先级比全局缓存策略设置的优先级要高。</p>
|
||||
|
||||
<button class="ui button tiny" @click.prevent="addRef(false)" type="button">+添加缓存条件</button> <a href="" @click.prevent="addRef(true)">+添加不缓存条件</a>
|
||||
<button class="ui button tiny" @click.prevent="addRef(false)" type="button">+添加缓存条件</button> <a href="" @click.prevent="addRef(true)" style="font-size: 0.8em">+添加不缓存条件</a>
|
||||
</div>
|
||||
<div class="margin"></div>
|
||||
</div>`
|
||||
|
||||
@@ -276,6 +276,36 @@ Vue.component("http-cond-url-eq-index", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// 全站
|
||||
Vue.component("http-cond-url-all", {
|
||||
props: ["v-cond"],
|
||||
data: function () {
|
||||
let cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "prefix",
|
||||
value: "/",
|
||||
isCaseInsensitive: false
|
||||
}
|
||||
if (this.vCond != null && typeof this.vCond.value == "string") {
|
||||
cond.value = this.vCond.value
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeCaseInsensitive: function (isCaseInsensitive) {
|
||||
this.cond.isCaseInsensitive = isCaseInsensitive
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value" disabled="disabled" style="background: #eee"/>
|
||||
<p class="comment">支持全站所有URL。</p>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL精准匹配
|
||||
Vue.component("http-cond-url-eq", {
|
||||
props: ["v-cond"],
|
||||
@@ -408,6 +438,38 @@ Vue.component("http-cond-url-not-regexp", {
|
||||
</div>`
|
||||
})
|
||||
|
||||
// URL通配符
|
||||
Vue.component("http-cond-url-wildcard-match", {
|
||||
props: ["v-cond"],
|
||||
mounted: function () {
|
||||
this.$refs.valueInput.focus()
|
||||
},
|
||||
data: function () {
|
||||
let cond = {
|
||||
isRequest: true,
|
||||
param: "${requestPath}",
|
||||
operator: "wildcard match",
|
||||
value: "",
|
||||
isCaseInsensitive: false
|
||||
}
|
||||
if (this.vCond != null && typeof this.vCond.value == "string") {
|
||||
cond.value = this.vCond.value
|
||||
}
|
||||
return {
|
||||
cond: cond
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeCaseInsensitive: function (isCaseInsensitive) {
|
||||
this.cond.isCaseInsensitive = isCaseInsensitive
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<input type="hidden" name="condJSON" :value="JSON.stringify(cond)"/>
|
||||
<input type="text" v-model="cond.value" ref="valueInput"/>
|
||||
<p class="comment">匹配URL的通配符,用星号(<code-label>*</code-label>)表示任意字符,比如(<code-label>/images/*.png</code-label>、<code-label>/static/*</code-label>,不需要带域名。</p>
|
||||
</div>`
|
||||
})
|
||||
|
||||
// User-Agent正则匹配
|
||||
Vue.component("http-cond-user-agent-regexp", {
|
||||
|
||||
@@ -184,7 +184,7 @@ Vue.component("http-header-policy-box", {
|
||||
<div v-if="((!vIsLocation && !vIsGroup) || requestHeaderRef.isPrior) && type == 'request'">
|
||||
<div v-if="vHasGroupRequestConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#request'">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#request'">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
<div :class="{'opacity-mask': vHasGroupRequestConfig}">
|
||||
<h4>设置请求Header <a href="" @click.prevent="addSettingHeader(vRequestHeaderPolicy.id)" style="font-size: 0.8em">[添加新Header]</a></h4>
|
||||
@@ -257,7 +257,7 @@ Vue.component("http-header-policy-box", {
|
||||
<div v-if="((!vIsLocation && !vIsGroup) || responseHeaderRef.isPrior) && type == 'response'">
|
||||
<div v-if="vHasGroupResponseConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#response'">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="vGroupSettingUrl + '#response'">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
<div :class="{'opacity-mask': vHasGroupResponseConfig}">
|
||||
<h4>设置响应Header <a href="" @click.prevent="addSettingHeader(vResponseHeaderPolicy.id)" style="font-size: 0.8em">[添加新Header]</a></h4>
|
||||
|
||||
@@ -119,13 +119,13 @@ Vue.component("http-websocket-box", {
|
||||
<more-options-tbody @change="changeAdvancedVisible" v-show="isOn()"></more-options-tbody>
|
||||
<tbody v-show="isOn() && advancedVisible">
|
||||
<tr>
|
||||
<td class="color-border">是否传递请求来源域</td>
|
||||
<td class="color-border">传递请求来源域</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" v-model="websocketConfig.requestSameOrigin"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">选中表示把接收到的请求中的<span class="ui label tiny">Origin</span>字段传递到源站。</p>
|
||||
<p class="comment">选中后,表示把接收到的请求中的<code-label>Origin</code-label>字段传递到源站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -99,27 +99,30 @@ Vue.component("origin-list-table", {
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="origin in vOrigins">
|
||||
<td :class="{disabled:!origin.isOn}">
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)" :class="{disabled:!origin.isOn}">{{origin.addr}} <i class="icon expand small"></i></a>
|
||||
<div style="margin-top: 0.3em">
|
||||
<tiny-basic-label v-if="origin.name.length > 0">{{origin.name}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.hasCert">证书</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.host != null && origin.host.length > 0">主机名: {{origin.host}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.followPort">端口跟随</tiny-basic-label>
|
||||
|
||||
<span v-if="origin.domains != null && origin.domains.length > 0"><tiny-basic-label v-for="domain in origin.domains">匹配: {{domain}}</tiny-basic-label></span>
|
||||
<span v-else-if="hasMatchedDomains"><tiny-basic-label>匹配: 所有域名</tiny-basic-label></span>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!origin.isOn}">{{origin.weight}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="origin.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteOrigin(origin.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr v-for="origin in vOrigins">
|
||||
<td :class="{disabled:!origin.isOn}">
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)" :class="{disabled:!origin.isOn}">{{origin.addr}} <i class="icon expand small"></i></a>
|
||||
<div style="margin-top: 0.3em">
|
||||
<tiny-basic-label v-if="origin.isOSS"><i class="icon hdd outline"></i>对象存储</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.name.length > 0">{{origin.name}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.hasCert">证书</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.host != null && origin.host.length > 0">主机名: {{origin.host}}</tiny-basic-label>
|
||||
<tiny-basic-label v-if="origin.followPort">端口跟随</tiny-basic-label>
|
||||
|
||||
<span v-if="origin.domains != null && origin.domains.length > 0"><tiny-basic-label v-for="domain in origin.domains">匹配: {{domain}}</tiny-basic-label></span>
|
||||
<span v-else-if="hasMatchedDomains"><tiny-basic-label>匹配: 所有域名</tiny-basic-label></span>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="{disabled:!origin.isOn}">{{origin.weight}}</td>
|
||||
<td>
|
||||
<label-on :v-is-on="origin.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="updateOrigin(origin.id)">修改</a>
|
||||
<a href="" @click.prevent="deleteOrigin(origin.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`
|
||||
})
|
||||
@@ -2,7 +2,8 @@ Vue.component("ssl-config-box", {
|
||||
props: [
|
||||
"v-ssl-policy",
|
||||
"v-protocol",
|
||||
"v-server-id"
|
||||
"v-server-id",
|
||||
"v-support-http3"
|
||||
],
|
||||
created: function () {
|
||||
let that = this
|
||||
@@ -26,6 +27,7 @@ Vue.component("ssl-config-box", {
|
||||
cipherSuitesIsOn: false,
|
||||
cipherSuites: [],
|
||||
http2Enabled: true,
|
||||
http3Enabled: false,
|
||||
ocspIsOn: false
|
||||
}
|
||||
} else {
|
||||
@@ -403,6 +405,15 @@ Vue.component("ssl-config-box", {
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="vProtocol == 'https' && vSupportHttp3">
|
||||
<td class="title">启用HTTP/3</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="policy.http3Enabled"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">选择证书</td>
|
||||
<td>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"type": "url-extension",
|
||||
"name": "URL扩展名",
|
||||
"name": "文件扩展名",
|
||||
"description": "根据URL中的文件路径扩展名进行过滤",
|
||||
"component": "http-cond-url-extension",
|
||||
"paramsTitle": "扩展名列表",
|
||||
@@ -16,6 +16,15 @@
|
||||
"isRequest": true,
|
||||
"caseInsensitive": false
|
||||
},
|
||||
{
|
||||
"type": "url-all",
|
||||
"name": "全站",
|
||||
"description": "全站所有URL",
|
||||
"component": "http-cond-url-all",
|
||||
"paramsTitle": "URL完整路径",
|
||||
"isRequest": true,
|
||||
"caseInsensitive": false
|
||||
},
|
||||
{
|
||||
"type": "url-prefix",
|
||||
"name": "URL前缀",
|
||||
@@ -43,6 +52,15 @@
|
||||
"isRequest": true,
|
||||
"caseInsensitive": true
|
||||
},
|
||||
{
|
||||
"type": "url-wildcard-match",
|
||||
"name": "URL通配符",
|
||||
"description": "使用通配符检查URL中的文件路径是否一致",
|
||||
"component": "http-cond-url-wildcard-match",
|
||||
"paramsTitle": "通配符",
|
||||
"isRequest": true,
|
||||
"caseInsensitive": true
|
||||
},
|
||||
{
|
||||
"type": "user-agent-regexp",
|
||||
"name": "User-Agent正则匹配",
|
||||
|
||||
@@ -610,12 +610,12 @@ window.teaweb = {
|
||||
let name = options.name
|
||||
let values = options.values
|
||||
let xFunc = options.x
|
||||
let xColorFunc = options.xColor
|
||||
let tooltipFunc = options.tooltip
|
||||
let axis = options.axis
|
||||
let valueFunc = options.value
|
||||
let max = options.max
|
||||
let interval = options.interval
|
||||
|
||||
let left = options.left
|
||||
if (typeof left != "number") {
|
||||
left = 0
|
||||
@@ -636,7 +636,10 @@ window.teaweb = {
|
||||
xAxis: {
|
||||
data: values.map(xFunc),
|
||||
axisLabel: {
|
||||
interval: interval
|
||||
interval: interval,
|
||||
textStyle: {
|
||||
color: xColorFunc
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
@@ -1044,6 +1047,20 @@ window.teaweb = {
|
||||
}
|
||||
})
|
||||
return isOk
|
||||
},
|
||||
playAlert: function () {
|
||||
let audioBox = document.createElement("AUDIO")
|
||||
audioBox.setAttribute("control", "")
|
||||
audioBox.setAttribute("autoplay", "")
|
||||
audioBox.innerHTML = "<source src=\"/audios/alert.ogg\" type=\"audio/ogg\"/>";
|
||||
document.body.appendChild(audioBox);
|
||||
audioBox.play().then(function () {
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(audioBox);
|
||||
}, 2000);
|
||||
}).catch(function (e) {
|
||||
console.log(e.message);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
web/public/js/utils.min.js
vendored
Normal file
1
web/public/js/utils.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -16,7 +16,7 @@
|
||||
{$echo "header"}
|
||||
<script type="text/javascript" src="/_/@default/@layout.js"></script>
|
||||
<script type="text/javascript" src="/js/components.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
|
||||
<script type="text/javascript" src="/js/date.tea.js"></script>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{$echo "header"}
|
||||
<script type="text/javascript" src="/_/@default/@layout.js"></script>
|
||||
<script type="text/javascript" src="/js/components.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
|
||||
<script type="text/javascript" src="/js/date.tea.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<first-menu>
|
||||
<menu-item href="/api">节点列表</menu-item>
|
||||
<span class="item">|</span>
|
||||
<menu-item :href="'/api/node?nodeId=' + node.id" code="index">"{{node.name}}"详情</menu-item>
|
||||
<menu-item :href="'/api/node/logs?nodeId=' + node.id" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/api/node/install?nodeId=' + node.id" code="install">安装节点</menu-item>
|
||||
<menu-item :href="'/api/node/update?nodeId=' + node.id" code="update">修改节点</menu-item>
|
||||
</first-menu>
|
||||
@@ -146,7 +146,7 @@ Tea.context(function () {
|
||||
return
|
||||
case "RPC_TEST_FAILED":
|
||||
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||
window.location = "/api"
|
||||
window.location = "/settings/api"
|
||||
})
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -142,7 +142,7 @@ Tea.context(function () {
|
||||
return
|
||||
case "RPC_TEST_FAILED":
|
||||
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||
window.location = "/api"
|
||||
window.location = "/settings/api"
|
||||
})
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -89,7 +89,7 @@ Tea.context(function () {
|
||||
return
|
||||
case "RPC_TEST_FAILED":
|
||||
teaweb.confirm("html:要安装的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||
window.location = "/api"
|
||||
window.location = "/settings/api"
|
||||
})
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">页面提示</td>
|
||||
<td class="color-border">未绑定域名页面提示</td>
|
||||
<td>
|
||||
<textarea name="httpAllDomainMismatchActionContentHTML" v-model="httpAllDomainMismatchActionContentHTML"></textarea>
|
||||
<textarea rows="3" name="httpAllDomainMismatchActionContentHTML" v-model="httpAllDomainMismatchActionContentHTML"></textarea>
|
||||
<p class="comment">访问未绑定的域名时提示的文字,可以使用HTML;仅限于HTTP请求,不适于用HTTPS(HTTPS会提示证书错误)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -31,6 +31,14 @@
|
||||
<p class="comment">允许这些域名不经过绑定就可以直接访问网站。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">默认域名</td>
|
||||
<td>
|
||||
<input type="text" name="httpAllDefaultDomain" v-model="config.httpAll.defaultDomain"/>
|
||||
<p class="comment">例外域名或使用节点IP访问时使用的默认域名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">允许使用节点IP访问</td>
|
||||
<td>
|
||||
@@ -39,10 +47,17 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly">
|
||||
<td class="color-border">默认域名</td>
|
||||
<td class="color-border">访问节点IP显示自定义内容</td>
|
||||
<td>
|
||||
<input type="text" name="httpAllDefaultDomain" v-model="config.httpAll.defaultDomain"/>
|
||||
<p class="comment">例外域名或使用节点IP访问时使用的默认域名。</p>
|
||||
<checkbox name="httpAllNodeIPShowPage" v-model="config.httpAll.nodeIPShowPage"></checkbox>
|
||||
<p class="comment">选中后,表示用户访问节点IP时显示自定义内容。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="config.httpAll.matchDomainStrictly && config.httpAll.nodeIPShowPage">
|
||||
<td class="color-border">访问节点IP自定义内容</td>
|
||||
<td>
|
||||
<textarea name="httpAllNodeIPPageHTML" v-model="config.httpAll.nodeIPPageHTML" rows="3" placeholder="访问节点IP时要显示的自定义内容"></textarea>
|
||||
<p class="comment">访问节点IP时要显示的自定义内容,支持HTML。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -138,6 +153,13 @@
|
||||
<p class="comment">选中后,表示找不到证书时自动查找其他网站设置的证书。此功能仅仅为了兼容以往系统版本,可能会导致用户访问的网站混乱,所以请不要轻易启用。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>强制Ln请求</td>
|
||||
<td>
|
||||
<checkbox name="httpAllForceLnRequest" v-model="config.httpAll.forceLnRequest"></checkbox>
|
||||
<p class="comment">选中后,所有请求优先发送到L2或者更高级别节点。默认不开启的情况下,只有可以缓存的内容请求才会发送到L2或者更高级别节点。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
|
||||
@@ -132,7 +132,7 @@ Tea.context(function () {
|
||||
return
|
||||
case "RPC_TEST_FAILED":
|
||||
teaweb.confirm("html:要升级的节点到API服务之间的RPC通讯测试失败,具体错误:" + errMsg + ",<br/>现在修改API信息?", function () {
|
||||
window.location = "/api"
|
||||
window.location = "/settings/api"
|
||||
})
|
||||
return
|
||||
default:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="ui icon message error" v-if="!isLoading && newVersionCode.length > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
升级提醒:有新版本管理系统可以更新:v{{currentVersionCode}} -> v{{newVersionCode}}
|
||||
<a href="https://goedge.cn/docs/Releases/Index.md?nav=1" target="_blank">[去官网查看]</a> <a :href="newVersionDownloadURL" target="_blank">[直接下载]</a>
|
||||
<a href="/settings/updates?doCheck=1">[查看详情]</a>
|
||||
|
||||
<a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
<!-- API节点升级提醒 -->
|
||||
<div class="ui icon message error" v-if="!isLoading && apiNodeUpgradeInfo.count > 0">
|
||||
<i class="icon warning circle"></i>
|
||||
<a href="/api">升级提醒:有 {{apiNodeUpgradeInfo.count}} 个API节点需要升级到 v{{apiNodeUpgradeInfo.version}} 版本;如果已经升级,请尝试重启API节点进程。</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
<a href="/settings/api">升级提醒:有 {{apiNodeUpgradeInfo.count}} 个API节点需要升级到 v{{apiNodeUpgradeInfo.version}} 版本;如果已经升级,请尝试重启API节点进程。</a><a href="" title="关闭" @click.prevent="closeMessage"><i class="ui icon remove small"></i></a>
|
||||
</div>
|
||||
|
||||
<!-- 本地API节点 -->
|
||||
@@ -72,9 +72,9 @@
|
||||
</div>
|
||||
|
||||
<div class="ui column with-border">
|
||||
<h4>API节点<link-icon href="/api" v-if="dashboard.canGoSettings"></link-icon></h4>
|
||||
<h4>API节点<link-icon href="/settings/api" v-if="dashboard.canGoSettings"></link-icon></h4>
|
||||
<div class="value"><span>{{dashboard.countAPINodes}}</span>个
|
||||
<span style="font-size: 1em" v-if="dashboard.countOfflineAPINodes > 0">/ <a href="/api" v-if="dashboard.canGoSettings"><span class="red" style="font-size: 1em">{{dashboard.countOfflineAPINodes}}离线</span></a><span class="red" style="font-size: 1em" v-else>{{dashboard.countOfflineAPINodes}}离线</span></span>
|
||||
<span style="font-size: 1em" v-if="dashboard.countOfflineAPINodes > 0">/ <a href="/settings/api" v-if="dashboard.canGoSettings"><span class="red" style="font-size: 1em">{{dashboard.countOfflineAPINodes}}离线</span></a><span class="red" style="font-size: 1em" v-else>{{dashboard.countOfflineAPINodes}}离线</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{$TEA.VUE}
|
||||
{$TEA.SEMANTIC}
|
||||
<script type="text/javascript" src="/js/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
|
||||
<script type="text/javascript" src="/js/components.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{$TEA.VUE}
|
||||
{$TEA.SEMANTIC}
|
||||
<script type="text/javascript" src="/js/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
|
||||
<script type="text/javascript" src="/js/components.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{$TEA.VUE}
|
||||
{$TEA.SEMANTIC}
|
||||
<script type="text/javascript" src="/js/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.min.js"></script>
|
||||
<script type="text/javascript" src="/js/sweetalert2/dist/sweetalert2.all.min.js"></script>
|
||||
<script type="text/javascript" src="/js/components.js"></script>
|
||||
<link rel="stylesheet" href="/_/@default/@layout.css"/>
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td>源站协议 *</td>
|
||||
<td class="title">源站协议 *</td>
|
||||
<td>
|
||||
<!-- HTTP -->
|
||||
<select class="ui dropdown auto-width" name="protocol" v-if="serverType == 'httpProxy' || serverType == 'httpWeb'" @change="changeProtocol" v-model="protocol">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
|
||||
<!-- 对象存储 -->
|
||||
<optgroup label="对象存储" v-if="ossTypes.length > 0"></optgroup>
|
||||
<option v-for="ossType in ossTypes" :value="ossType.code">{{ossType.name}}</option>
|
||||
</select>
|
||||
|
||||
<!-- TCP -->
|
||||
@@ -25,20 +29,26 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<!-- 普通源站 -->
|
||||
<tr v-show="!isOSS">
|
||||
<td class="title">源站地址 *</td>
|
||||
<td>
|
||||
<input type="text" name="addr" ref="focus" v-model="addr" @input="changeAddr"/>
|
||||
<p class="comment"><span class="red" v-if="addrError.length > 0">{{addrError}}</span>源站服务器地址,通常是一个IP(或域名)加端口<span v-if="serverType == 'httpProxy'">,不需要加 http:// 或 https://</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP || protocol == 'tls'">
|
||||
<tr v-if="(isHTTP || protocol == 'tls') && !isOSS">
|
||||
<td>回源主机名</td>
|
||||
<td>
|
||||
<input type="text" name="host" placeholder="比如example.com" maxlength="100"/>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置源站接收到的域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置访问源站的站点域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- OSS -->
|
||||
{$ .ossForm}
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
@@ -56,7 +66,7 @@
|
||||
<p class="comment">默认不需要填写,表示支持所有域名。如果填写了专属域名,表示这些源站只会在所列的专属域名被访问时才生效。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="!isOSS">
|
||||
<td>端口跟随</td>
|
||||
<td>
|
||||
<checkbox name="followPort"></checkbox>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Tea.context(function () {
|
||||
this.addr = ""
|
||||
this.protocol = ""
|
||||
this.isOSS = false
|
||||
|
||||
this.addrError = ""
|
||||
|
||||
@@ -15,6 +16,8 @@ Tea.context(function () {
|
||||
}
|
||||
|
||||
this.changeProtocol = function () {
|
||||
this.isOSS = this.protocol.startsWith("oss:")
|
||||
|
||||
this.checkPort()
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
<input type="hidden" name="origins" :value="JSON.stringify(origins)"/>
|
||||
<div v-if="origins.length > 0">
|
||||
<div v-for="(origin, index) in origins" class="ui label small basic">
|
||||
{{origin.addr.protocol}}://{{origin.addr.host}}:{{origin.addr.portRange}}
|
||||
<span v-if="origin.addrSummary != null && origin.addrSummary.length > 0">{{origin.addrSummary}}</span>
|
||||
<span v-else>{{origin.addr.protocol}}://{{origin.addr.host}}:{{origin.addr.portRange}}</span>
|
||||
<a href="" title="删除" @click.prevent="removeOrigin(index)"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="right-box with-menu">
|
||||
<div v-if="hasGroupConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
|
||||
<div :class="{'opacity-mask': hasGroupConfig}">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<div v-if="hasGroupConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
|
||||
<div :class="{'opacity-mask': hasGroupConfig}">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="right-box with-menu">
|
||||
<div v-if="hasGroupConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
|
||||
<div :class="{'opacity-mask': hasGroupConfig}">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="right-box with-menu">
|
||||
<div v-if="hasGroupConfig">
|
||||
<div class="margin"></div>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">服务分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
|
||||
</div>
|
||||
|
||||
<div :class="{'opacity-mask': hasGroupConfig}">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</table>
|
||||
|
||||
<!-- SSL配置 -->
|
||||
<ssl-config-box :v-ssl-policy="httpsConfig.sslPolicy" :v-protocol="'https'" v-show="httpsConfig.isOn" :v-server-id="serverId"></ssl-config-box>
|
||||
<ssl-config-box :v-ssl-policy="httpsConfig.sslPolicy" :v-protocol="'https'" v-show="httpsConfig.isOn" :v-server-id="serverId" :v-support-http3="httpsConfig.supportsHTTP3"></ssl-config-box>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
|
||||
@@ -7,12 +7,16 @@
|
||||
<input type="hidden" name="originType" :value="originType"/>
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td>源站协议</td>
|
||||
<td class="title">源站协议</td>
|
||||
<td>
|
||||
<!-- HTTP -->
|
||||
<select class="ui dropdown auto-width" name="protocol" v-model="protocol" v-if="serverType == 'httpProxy' || serverType == 'httpWeb'" @change="changeProtocol">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
|
||||
<!-- 对象存储 -->
|
||||
<optgroup label="对象存储" v-if="ossTypes.length > 0"></optgroup>
|
||||
<option v-for="ossType in ossTypes" :value="ossType.code">{{ossType.name}}</option>
|
||||
</select>
|
||||
|
||||
<!-- TCP -->
|
||||
@@ -27,7 +31,9 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<!-- 普通源站 -->
|
||||
<tr v-show="!isOSS">
|
||||
<td class="title">源站地址 *</td>
|
||||
<td>
|
||||
<input type="text" name="addr" ref="focus" v-model="addr" @input="changeAddr"/>
|
||||
@@ -35,13 +41,17 @@
|
||||
源站服务器地址,通常是一个IP(或域名)加端口<span v-if="serverType == 'httpProxy'">,不需要加 http:// 或 https://</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP || protocol == 'tls'">
|
||||
<tr v-if="(isHTTP || protocol == 'tls') && !isOSS">
|
||||
<td>回源主机名</td>
|
||||
<td>
|
||||
<input type="text" name="host" placeholder="比如example.com" maxlength="100"/>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置源站接收到的域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置访问源站的站点域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- OSS -->
|
||||
{$ .ossForm}
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
@@ -59,7 +69,7 @@
|
||||
<p class="comment">默认不需要填写,表示支持所有域名。如果填写了专属域名,表示这些源站只会在所列的专属域名被访问时才生效。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="!isOSS">
|
||||
<td>端口跟随</td>
|
||||
<td>
|
||||
<checkbox name="followPort"></checkbox>
|
||||
@@ -80,7 +90,7 @@
|
||||
<p class="comment">给当前源站起一个容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="!isOSS">
|
||||
<td>连接失败超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -94,7 +104,7 @@
|
||||
<p class="comment">连接源站失败的最大超时时间,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>读取超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -108,7 +118,7 @@
|
||||
<p class="comment">读取内容时的最大超时时间,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大并发连接数</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -119,7 +129,7 @@
|
||||
<p class="comment">源站可以接受到的最大并发连接数,0表示使用系统默认。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大空闲连接数</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -130,7 +140,7 @@
|
||||
<p class="comment">当没有请求时,源站保持等待的最大空闲连接数量,0表示使用系统默认。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大空闲超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -151,7 +161,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>启用当前源站</td>
|
||||
<td>
|
||||
<checkbox name="isOn" :value="true"></checkbox>
|
||||
</td>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
Tea.context(function () {
|
||||
this.addr = ""
|
||||
this.protocol = ""
|
||||
this.isOSS = false
|
||||
|
||||
this.addrError = ""
|
||||
|
||||
@@ -13,6 +14,8 @@ Tea.context(function () {
|
||||
}
|
||||
|
||||
this.changeProtocol = function () {
|
||||
this.isOSS = this.protocol.startsWith("oss:")
|
||||
|
||||
this.checkPort()
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
|
||||
<table class="ui table selectable definition">
|
||||
<tr>
|
||||
<td>源站协议</td>
|
||||
<td class="title">源站协议</td>
|
||||
<td>
|
||||
<!-- HTTP -->
|
||||
<select class="ui dropdown auto-width" name="protocol" v-model="origin.protocol" v-if="serverType == 'httpProxy' || serverType == 'httpWeb'" @change="changeProtocol">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
|
||||
<!-- 对象存储 -->
|
||||
<optgroup label="对象存储" v-if="ossTypes.length > 0"></optgroup>
|
||||
<option v-for="ossType in ossTypes" :value="ossType.code">{{ossType.name}}</option>
|
||||
</select>
|
||||
|
||||
<!-- TCP -->
|
||||
@@ -29,20 +33,26 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<!-- 普通源站 -->
|
||||
<tr v-show="!isOSS">
|
||||
<td class="title">源站地址 *</td>
|
||||
<td>
|
||||
<input type="text" name="addr" ref="focus" v-model="origin.addr" @input="changeAddr"/>
|
||||
<p class="comment"><span class="red" v-if="addrError.length > 0">{{addrError}}</span>源站服务器地址,通常是一个IP(或域名)加端口<span v-if="serverType == 'httpProxy'">,不需要加 http:// 或 https://</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP || origin.protocol == 'tls'">
|
||||
<tr v-if="(isHTTP || origin.protocol == 'tls') && !isOSS">
|
||||
<td>回源主机名</td>
|
||||
<td>
|
||||
<input type="text" name="host" v-model="origin.host" placeholder="比如example.com" maxlength="100"/>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置源站接收到的域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
<p class="comment">请求源站时的Host字段值,用于设置访问源站的站点域名<span v-if="isHTTP">,支持请求变量</span>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- OSS -->
|
||||
{$ .ossForm}
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
@@ -60,7 +70,7 @@
|
||||
<p class="comment">默认不需要填写,表示支持所有域名。如果填写了专属域名,表示这些源站只会在所列的专属域名被访问时才生效。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="!isOSS">
|
||||
<td>端口跟随</td>
|
||||
<td>
|
||||
<checkbox name="followPort" v-model="origin.followPort"></checkbox>
|
||||
@@ -81,7 +91,7 @@
|
||||
<p class="comment">给当前源站起一个容易识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr v-show="!isOSS">
|
||||
<td>连接失败超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -95,7 +105,7 @@
|
||||
<p class="comment">连接源站失败的最大超时时间,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>读取超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -109,7 +119,7 @@
|
||||
<p class="comment">读取内容时的最大超时时间,0表示不限制。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大并发连接数</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -120,7 +130,7 @@
|
||||
<p class="comment">源站可以接受到的最大并发连接数,0表示使用系统默认。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大空闲连接数</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -131,7 +141,7 @@
|
||||
<p class="comment">当没有请求时,源站保持等待的最大空闲连接数量,0表示使用系统默认。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isHTTP">
|
||||
<tr v-if="isHTTP && !isOSS">
|
||||
<td>最大空闲超时时间</td>
|
||||
<td>
|
||||
<div class="ui fields inline">
|
||||
@@ -152,7 +162,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>是否启用</td>
|
||||
<td>启用当前源站</td>
|
||||
<td>
|
||||
<checkbox name="isOn" :value="origin.isOn"></checkbox>
|
||||
</td>
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
Tea.context(function () {
|
||||
this.addrError = ""
|
||||
this.isOSS = this.origin != null && this.origin.protocol != null && this.origin.protocol.startsWith("oss:")
|
||||
|
||||
// 预先设置oss选项
|
||||
if (!this.isOSS) {
|
||||
this.origin.oss = {
|
||||
type: "",
|
||||
options: {}
|
||||
}
|
||||
}
|
||||
|
||||
this.$delay(function () {
|
||||
this.checkPort()
|
||||
})
|
||||
|
||||
this.changeProtocol = function () {
|
||||
this.isOSS = this.origin.protocol.startsWith("oss:")
|
||||
|
||||
this.checkPort()
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user