mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +08:00 
			
		
		
		
	增加DEMO模式、修复监控节点、用户节点、认证节点无法查看运行日志的Bug
This commit is contained in:
		@@ -57,6 +57,19 @@ func main() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Println("enter recovery mode successfully")
 | 
							fmt.Println("enter recovery mode successfully")
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						app.On("demo", func() {
 | 
				
			||||||
 | 
							sock := gosock.NewTmpSock(teaconst.ProcessName)
 | 
				
			||||||
 | 
							if !sock.IsListening() {
 | 
				
			||||||
 | 
								fmt.Println("[ERROR]the service not started yet, you should start the service first")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err := sock.Send(&gosock.Command{Code: "demo"})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Println("[ERROR]change demo mode failed: " + err.Error())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Println("change demo mode successfully")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	app.Run(func() {
 | 
						app.Run(func() {
 | 
				
			||||||
		adminNode := nodes.NewAdminNode()
 | 
							adminNode := nodes.NewAdminNode()
 | 
				
			||||||
		adminNode.Run()
 | 
							adminNode.Run()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -12,7 +12,7 @@ require (
 | 
				
			|||||||
	github.com/golang/protobuf v1.5.2
 | 
						github.com/golang/protobuf v1.5.2
 | 
				
			||||||
	github.com/google/go-cmp v0.5.6 // indirect
 | 
						github.com/google/go-cmp v0.5.6 // indirect
 | 
				
			||||||
	github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa
 | 
						github.com/iwind/TeaGo v0.0.0-20210720011303-fc255c995afa
 | 
				
			||||||
	github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f
 | 
						github.com/iwind/gosock v0.0.0-20210721090148-db2e77703143
 | 
				
			||||||
	github.com/miekg/dns v1.1.35
 | 
						github.com/miekg/dns v1.1.35
 | 
				
			||||||
	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 | 
						github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 | 
				
			||||||
	github.com/tealeg/xlsx/v3 v3.2.3
 | 
						github.com/tealeg/xlsx/v3 v3.2.3
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@@ -68,6 +68,8 @@ github.com/iwind/gosock v0.0.0-20210706152215-08fe64b7a8a3 h1:BVHIjJU5RUX0grmvDt
 | 
				
			|||||||
github.com/iwind/gosock v0.0.0-20210706152215-08fe64b7a8a3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
 | 
					github.com/iwind/gosock v0.0.0-20210706152215-08fe64b7a8a3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
 | 
				
			||||||
github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f h1:+mKLTd5tCCLXK+iY5Xjz58hau6qFv9chGqcZ4FWUiIs=
 | 
					github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f h1:+mKLTd5tCCLXK+iY5Xjz58hau6qFv9chGqcZ4FWUiIs=
 | 
				
			||||||
github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
 | 
					github.com/iwind/gosock v0.0.0-20210720054405-4030f271aa3f/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
 | 
				
			||||||
 | 
					github.com/iwind/gosock v0.0.0-20210721090148-db2e77703143 h1:RXINu3KVasw6MWv8MA6ne5ur84YhTAzG/j68YEM0KuI=
 | 
				
			||||||
 | 
					github.com/iwind/gosock v0.0.0-20210721090148-db2e77703143/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
 | 
				
			||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
				
			||||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
 | 
					github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
 | 
				
			||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
// +build demo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package teaconst
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	IsDemo = true
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
// +build !demo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package teaconst
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	IsDemo = false
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
@@ -5,3 +5,8 @@ package teaconst
 | 
				
			|||||||
var (
 | 
					var (
 | 
				
			||||||
	IsRecoverMode = false
 | 
						IsRecoverMode = false
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						IsDemoMode         = false
 | 
				
			||||||
 | 
						ErrorDemoOperation = "DEMO模式下无法进行创建、修改、删除等操作"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -323,6 +323,9 @@ func (this *AdminNode) listenSock() error {
 | 
				
			|||||||
			case "recover":
 | 
								case "recover":
 | 
				
			||||||
				teaconst.IsRecoverMode = true
 | 
									teaconst.IsRecoverMode = true
 | 
				
			||||||
				_ = cmd.ReplyOk()
 | 
									_ = cmd.ReplyOk()
 | 
				
			||||||
 | 
								case "demo":
 | 
				
			||||||
 | 
									teaconst.IsDemoMode = !teaconst.IsDemoMode
 | 
				
			||||||
 | 
									_ = cmd.ReplyOk()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,9 @@ func (this *IndexAction) RunGet(params struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Auth *helpers.UserShouldAuth
 | 
						Auth *helpers.UserShouldAuth
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
 | 
						// DEMO模式
 | 
				
			||||||
 | 
						this.Data["isDemo"] = teaconst.IsDemoMode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 检查系统是否已经配置过
 | 
						// 检查系统是否已经配置过
 | 
				
			||||||
	if !setup.IsConfigured() {
 | 
						if !setup.IsConfigured() {
 | 
				
			||||||
		this.RedirectURL("/setup")
 | 
							this.RedirectURL("/setup")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,7 +132,7 @@ func (this *CreateAction) RunPost(params struct {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	case serverconfigs.ServerTypeTCPProxy:
 | 
						case serverconfigs.ServerTypeTCPProxy:
 | 
				
			||||||
		// 在DEMO模式下不能创建
 | 
							// 在DEMO模式下不能创建
 | 
				
			||||||
		if teaconst.IsDemo {
 | 
							if teaconst.IsDemoMode {
 | 
				
			||||||
			this.Fail("DEMO模式下不能创建TCP反向代理")
 | 
								this.Fail("DEMO模式下不能创建TCP反向代理")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,7 +169,7 @@ func (this *CreateAction) RunPost(params struct {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	case serverconfigs.ServerTypeUDPProxy:
 | 
						case serverconfigs.ServerTypeUDPProxy:
 | 
				
			||||||
		// 在DEMO模式下不能创建
 | 
							// 在DEMO模式下不能创建
 | 
				
			||||||
		if teaconst.IsDemo {
 | 
							if teaconst.IsDemoMode {
 | 
				
			||||||
			this.Fail("DEMO模式下不能创建UDP反向代理")
 | 
								this.Fail("DEMO模式下不能创建UDP反向代理")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,20 +31,20 @@ func (this *LogsAction) RunGet(params struct {
 | 
				
			|||||||
	this.Data["keyword"] = params.Keyword
 | 
						this.Data["keyword"] = params.Keyword
 | 
				
			||||||
	this.Data["level"] = params.Level
 | 
						this.Data["level"] = params.Level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
 | 
						authorityNodeResp, err := this.RPC().AuthorityNodeRPC().FindEnabledAuthorityNode(this.AdminContext(), &pb.FindEnabledAuthorityNodeRequest{NodeId: params.NodeId})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.ErrorPage(err)
 | 
							this.ErrorPage(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiNode := apiNodeResp.Node
 | 
						authorityNode := authorityNodeResp.Node
 | 
				
			||||||
	if apiNode == nil {
 | 
						if authorityNode == nil {
 | 
				
			||||||
		this.NotFound("apiNode", params.NodeId)
 | 
							this.NotFound("authorityNode", params.NodeId)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Data["node"] = maps.Map{
 | 
						this.Data["node"] = maps.Map{
 | 
				
			||||||
		"id":   apiNode.Id,
 | 
							"id":   authorityNode.Id,
 | 
				
			||||||
		"name": apiNode.Name,
 | 
							"name": authorityNode.Name,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
						countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,20 +31,20 @@ func (this *LogsAction) RunGet(params struct {
 | 
				
			|||||||
	this.Data["keyword"] = params.Keyword
 | 
						this.Data["keyword"] = params.Keyword
 | 
				
			||||||
	this.Data["level"] = params.Level
 | 
						this.Data["level"] = params.Level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
 | 
						monitorNodeResp, err := this.RPC().MonitorNodeRPC().FindEnabledMonitorNode(this.AdminContext(), &pb.FindEnabledMonitorNodeRequest{NodeId: params.NodeId})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.ErrorPage(err)
 | 
							this.ErrorPage(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiNode := apiNodeResp.Node
 | 
						monitorNode := monitorNodeResp.Node
 | 
				
			||||||
	if apiNode == nil {
 | 
						if monitorNode == nil {
 | 
				
			||||||
		this.NotFound("apiNode", params.NodeId)
 | 
							this.NotFound("monitorNode", params.NodeId)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Data["node"] = maps.Map{
 | 
						this.Data["node"] = maps.Map{
 | 
				
			||||||
		"id":   apiNode.Id,
 | 
							"id":   monitorNode.Id,
 | 
				
			||||||
		"name": apiNode.Name,
 | 
							"name": monitorNode.Name,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
						countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,20 +31,20 @@ func (this *LogsAction) RunGet(params struct {
 | 
				
			|||||||
	this.Data["keyword"] = params.Keyword
 | 
						this.Data["keyword"] = params.Keyword
 | 
				
			||||||
	this.Data["level"] = params.Level
 | 
						this.Data["level"] = params.Level
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiNodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{NodeId: params.NodeId})
 | 
						userNodeResp, err := this.RPC().UserNodeRPC().FindEnabledUserNode(this.AdminContext(), &pb.FindEnabledUserNodeRequest{NodeId: params.NodeId})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.ErrorPage(err)
 | 
							this.ErrorPage(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiNode := apiNodeResp.Node
 | 
						userNode := userNodeResp.Node
 | 
				
			||||||
	if apiNode == nil {
 | 
						if userNode == nil {
 | 
				
			||||||
		this.NotFound("apiNode", params.NodeId)
 | 
							this.NotFound("userNode", params.NodeId)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.Data["node"] = maps.Map{
 | 
						this.Data["node"] = maps.Map{
 | 
				
			||||||
		"id":   apiNode.Id,
 | 
							"id":   userNode.Id,
 | 
				
			||||||
		"name": apiNode.Name,
 | 
							"name": userNode.Name,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
						countResp, err := this.RPC().NodeLogRPC().CountNodeLogs(this.AdminContext(), &pb.CountNodeLogsRequest{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
	"github.com/iwind/TeaGo/maps"
 | 
						"github.com/iwind/TeaGo/maps"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 认证拦截
 | 
					// 认证拦截
 | 
				
			||||||
@@ -21,12 +22,32 @@ func NewUserMustAuth(module string) *userMustAuth {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramName string) (goNext bool) {
 | 
					func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramName string) (goNext bool) {
 | 
				
			||||||
 | 
						var action = actionPtr.Object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 恢复模式
 | 
				
			||||||
	if teaconst.IsRecoverMode {
 | 
						if teaconst.IsRecoverMode {
 | 
				
			||||||
		actionPtr.Object().RedirectURL("/recover")
 | 
							action.RedirectURL("/recover")
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
	var action = actionPtr.Object()
 | 
						// DEMO模式
 | 
				
			||||||
 | 
						if teaconst.IsDemoMode {
 | 
				
			||||||
 | 
							if action.Request.Method == http.MethodPost {
 | 
				
			||||||
 | 
								var actionName = action.Spec.ClassName[strings.LastIndex(action.Spec.ClassName, ".")+1:]
 | 
				
			||||||
 | 
								var denyPrefixes = []string{"Update", "Create", "Delete", "Truncate", "Clean", "Clear", "Reset", "Add", "Remove"}
 | 
				
			||||||
 | 
								for _, prefix := range denyPrefixes {
 | 
				
			||||||
 | 
									if strings.HasPrefix(actionName, prefix) {
 | 
				
			||||||
 | 
										action.Fail(teaconst.ErrorDemoOperation)
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if strings.Index(action.Spec.PkgPath, "settings") > 0 || strings.Index(action.Spec.PkgPath, "delete") > 0 || strings.Index(action.Spec.PkgPath, "update") > 0 {
 | 
				
			||||||
 | 
									action.Fail(teaconst.ErrorDemoOperation)
 | 
				
			||||||
 | 
									return false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 安全相关
 | 
						// 安全相关
 | 
				
			||||||
	securityConfig, _ := configloaders.LoadSecurityConfig()
 | 
						securityConfig, _ := configloaders.LoadSecurityConfig()
 | 
				
			||||||
@@ -114,7 +135,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
 | 
				
			|||||||
	action.Data["teaShowOpenSourceInfo"] = config.ShowOpenSourceInfo
 | 
						action.Data["teaShowOpenSourceInfo"] = config.ShowOpenSourceInfo
 | 
				
			||||||
	action.Data["teaIsSuper"] = false
 | 
						action.Data["teaIsSuper"] = false
 | 
				
			||||||
	action.Data["teaIsPlus"] = teaconst.IsPlus
 | 
						action.Data["teaIsPlus"] = teaconst.IsPlus
 | 
				
			||||||
	action.Data["teaDemoEnabled"] = teaconst.IsDemo
 | 
						action.Data["teaDemoEnabled"] = teaconst.IsDemoMode
 | 
				
			||||||
	action.Data["teaShowFinance"] = configloaders.ShowFinance()
 | 
						action.Data["teaShowFinance"] = configloaders.ShowFinance()
 | 
				
			||||||
	if !action.Data.Has("teaSubMenu") {
 | 
						if !action.Data.Has("teaSubMenu") {
 | 
				
			||||||
		action.Data["teaSubMenu"] = ""
 | 
							action.Data["teaSubMenu"] = ""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,11 @@ Tea.context(function () {
 | 
				
			|||||||
	this.passwordMd5 = ""
 | 
						this.passwordMd5 = ""
 | 
				
			||||||
	this.encodedFrom = window.encodeURIComponent(this.from)
 | 
						this.encodedFrom = window.encodeURIComponent(this.from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this.isDemo) {
 | 
				
			||||||
 | 
							this.username = "admin"
 | 
				
			||||||
 | 
							this.password = "123456"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.showOTP = false
 | 
						this.showOTP = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.isSubmitting = false
 | 
						this.isSubmitting = false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,7 +82,7 @@
 | 
				
			|||||||
						</td>
 | 
											</td>
 | 
				
			||||||
					</tr>
 | 
										</tr>
 | 
				
			||||||
					<tr>
 | 
										<tr>
 | 
				
			||||||
						<td>节点主机地址</td>
 | 
											<td>节点主机地址 *</td>
 | 
				
			||||||
						<td>
 | 
											<td>
 | 
				
			||||||
							<div class="ui fields inline">
 | 
												<div class="ui fields inline">
 | 
				
			||||||
								<div class="ui field">
 | 
													<div class="ui field">
 | 
				
			||||||
@@ -264,7 +264,7 @@
 | 
				
			|||||||
						</td>
 | 
											</td>
 | 
				
			||||||
					</tr>
 | 
										</tr>
 | 
				
			||||||
					<tr>
 | 
										<tr>
 | 
				
			||||||
						<td>节点主机地址</td>
 | 
											<td>节点主机地址 *</td>
 | 
				
			||||||
						<td>
 | 
											<td>
 | 
				
			||||||
							{{apiNodeInfo.newHost}}
 | 
												{{apiNodeInfo.newHost}}
 | 
				
			||||||
						</td>
 | 
											</td>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user