mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +08:00 
			
		
		
		
	实现迁移辅助功能(系统设置 -- 高级设置 -- 迁移)
This commit is contained in:
		@@ -40,6 +40,7 @@ func (this *AdvancedHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNex
 | 
			
		||||
		if teaconst.IsPlus {
 | 
			
		||||
			tabbar.Add("监控节点", "", "/settings/monitorNodes", "", this.tab == "monitorNodes")
 | 
			
		||||
		}
 | 
			
		||||
		tabbar.Add("迁移", "", "/settings/transfer", "", this.tab == "transfer")
 | 
			
		||||
		if teaconst.BuildPlus {
 | 
			
		||||
			tabbar.Add("商业版认证", "", "/settings/authority", "", this.tab == "authority")
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								internal/web/actions/default/settings/transfer/index.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								internal/web/actions/default/settings/transfer/index.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
 | 
			
		||||
type IndexAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) Init() {
 | 
			
		||||
	this.Nav("", "transfer", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *IndexAction) RunGet(params struct{}) {
 | 
			
		||||
	this.Show()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								internal/web/actions/default/settings/transfer/init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/web/actions/default/settings/transfer/init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
 | 
			
		||||
	"github.com/iwind/TeaGo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	TeaGo.BeforeStart(func(server *TeaGo.Server) {
 | 
			
		||||
		server.
 | 
			
		||||
			Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)).
 | 
			
		||||
			Helper(settingutils.NewAdvancedHelper("transfer")).
 | 
			
		||||
			Prefix("/settings/transfer").
 | 
			
		||||
			Get("", new(IndexAction)).
 | 
			
		||||
			Post("/validateAPI", new(ValidateAPIAction)).
 | 
			
		||||
			Post("/updateHosts", new(UpdateHostsAction)).
 | 
			
		||||
			Post("/upgradeNodes", new(UpgradeNodesAction)).
 | 
			
		||||
			Post("/statNodes", new(StatNodesAction)).
 | 
			
		||||
			EndAll()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								internal/web/actions/default/settings/transfer/statNodes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/web/actions/default/settings/transfer/statNodes.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StatNodesAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *StatNodesAction) RunPost(params struct{}) {
 | 
			
		||||
	countNodesResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ActiveState: 1})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["countNodes"] = countNodesResp.Count
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										158
									
								
								internal/web/actions/default/settings/transfer/updateHosts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								internal/web/actions/default/settings/transfer/updateHosts.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpdateHostsAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpdateHostsAction) RunPost(params struct {
 | 
			
		||||
	Protocol string
 | 
			
		||||
	Host     string
 | 
			
		||||
	Port     string
 | 
			
		||||
 | 
			
		||||
	OldHosts []string
 | 
			
		||||
	NewHosts []string
 | 
			
		||||
}) {
 | 
			
		||||
	if len(params.OldHosts) != len(params.NewHosts) {
 | 
			
		||||
		this.Fail("参数配置错误,请刷新页面后重试")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查端口
 | 
			
		||||
	config, err := configs.LoadAPIConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("加载当前平台的API配置失败:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	var apiURL = params.Protocol + "://" + configutils.QuoteIP(params.Host) + ":" + params.Port
 | 
			
		||||
	config.RPC.Endpoints = []string{apiURL}
 | 
			
		||||
	client, err := rpc.NewRPCClient(config, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("检查API节点地址出错:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		_ = client.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.FailField("host", "测试API节点时出错,请检查配置,错误信息:"+err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.APINodeRPC().FindCurrentAPINodeVersion(client.APIContext(0), &pb.FindCurrentAPINodeVersionRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.FailField("host", "无法连接此API节点,错误信息:"+err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		_ = client.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// API节点列表
 | 
			
		||||
	nodesResp, err := client.APINodeRPC().FindAllEnabledAPINodes(client.Context(0), &pb.FindAllEnabledAPINodesRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("获取API节点列表失败,错误信息:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	var endpoints = []string{}
 | 
			
		||||
	for _, node := range nodesResp.ApiNodes {
 | 
			
		||||
		if !node.IsOn {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// http
 | 
			
		||||
		if len(node.HttpJSON) > 0 {
 | 
			
		||||
			for index, oldHost := range params.OldHosts {
 | 
			
		||||
				if len(params.NewHosts[index]) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				node.HttpJSON = bytes.ReplaceAll(node.HttpJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\""))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// https
 | 
			
		||||
		if len(node.HttpsJSON) > 0 {
 | 
			
		||||
			for index, oldHost := range params.OldHosts {
 | 
			
		||||
				if len(params.NewHosts[index]) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				node.HttpsJSON = bytes.ReplaceAll(node.HttpsJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\""))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// restHTTP
 | 
			
		||||
		if len(node.RestHTTPJSON) > 0 {
 | 
			
		||||
			for index, oldHost := range params.OldHosts {
 | 
			
		||||
				if len(params.NewHosts[index]) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				node.RestHTTPJSON = bytes.ReplaceAll(node.RestHTTPJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\""))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// restHTTPS
 | 
			
		||||
		if len(node.RestHTTPSJSON) > 0 {
 | 
			
		||||
			for index, oldHost := range params.OldHosts {
 | 
			
		||||
				if len(params.NewHosts[index]) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				node.RestHTTPSJSON = bytes.ReplaceAll(node.RestHTTPSJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\""))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// access addrs
 | 
			
		||||
		if len(node.AccessAddrsJSON) > 0 {
 | 
			
		||||
			for index, oldHost := range params.OldHosts {
 | 
			
		||||
				if len(params.NewHosts[index]) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				node.AccessAddrsJSON = bytes.ReplaceAll(node.AccessAddrsJSON, []byte("\""+oldHost+"\""), []byte("\""+params.NewHosts[index]+"\""))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var addrs []*serverconfigs.NetworkAddressConfig
 | 
			
		||||
			err = json.Unmarshal(node.AccessAddrsJSON, &addrs)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点访问地址失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, addr := range addrs {
 | 
			
		||||
				err = addr.Init()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					// 暂时不提示错误
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				for _, a := range addr.FullAddresses() {
 | 
			
		||||
					if !lists.ContainsString(endpoints, a) {
 | 
			
		||||
						endpoints = append(endpoints, a)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 保存
 | 
			
		||||
		_, err = client.APINodeRPC().UpdateAPINode(client.Context(0), &pb.UpdateAPINodeRequest{
 | 
			
		||||
			ApiNodeId:       node.Id,
 | 
			
		||||
			Name:            node.Name,
 | 
			
		||||
			Description:     node.Description,
 | 
			
		||||
			HttpJSON:        node.HttpJSON,
 | 
			
		||||
			HttpsJSON:       node.HttpsJSON,
 | 
			
		||||
			AccessAddrsJSON: node.AccessAddrsJSON,
 | 
			
		||||
			IsOn:            node.IsOn,
 | 
			
		||||
			RestIsOn:        node.RestIsOn,
 | 
			
		||||
			RestHTTPJSON:    node.RestHTTPJSON,
 | 
			
		||||
			RestHTTPSJSON:   node.RestHTTPSJSON,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.Fail("保存API节点信息失败:" + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpgradeNodesAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *UpgradeNodesAction) RunPost(params struct {
 | 
			
		||||
	ApiNodeProtocol string
 | 
			
		||||
	ApiNodeHost     string
 | 
			
		||||
	ApiNodePort     int
 | 
			
		||||
}) {
 | 
			
		||||
	nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{
 | 
			
		||||
		ActiveState: 1,
 | 
			
		||||
		Size:        100,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var nodes = nodesResp.Nodes
 | 
			
		||||
	this.Data["hasNext"] = len(nodes) > 0
 | 
			
		||||
	this.Data["count"] = len(nodes)
 | 
			
		||||
 | 
			
		||||
	if len(nodes) > 0 {
 | 
			
		||||
		var message = &messageconfigs.ChangeAPINodeMessage{
 | 
			
		||||
			Addr: params.ApiNodeProtocol + "://" + configutils.QuoteIP(params.ApiNodeHost) + ":" + types.String(params.ApiNodePort),
 | 
			
		||||
		}
 | 
			
		||||
		messageJSON, err := json.Marshal(message)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			this.ErrorPage(err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, node := range nodesResp.Nodes {
 | 
			
		||||
			resp, err := this.RPC().NodeRPC().SendCommandToNode(this.AdminContext(), &pb.NodeStreamMessage{
 | 
			
		||||
				NodeId:         node.Id,
 | 
			
		||||
				TimeoutSeconds: 3,
 | 
			
		||||
				Code:           messageconfigs.MessageCodeChangeAPINode,
 | 
			
		||||
				DataJSON:       messageJSON,
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.ErrorPage(err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !resp.IsOk {
 | 
			
		||||
				this.Fail(resp.Message)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								internal/web/actions/default/settings/transfer/validateAPI.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								internal/web/actions/default/settings/transfer/validateAPI.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package transfer
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/actions"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
	"google.golang.org/grpc/codes"
 | 
			
		||||
	"google.golang.org/grpc/status"
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ValidateAPIAction struct {
 | 
			
		||||
	actionutils.ParentAction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ValidateAPIAction) RunPost(params struct {
 | 
			
		||||
	Host     string
 | 
			
		||||
	Port     string
 | 
			
		||||
	Protocol string
 | 
			
		||||
 | 
			
		||||
	Must *actions.Must
 | 
			
		||||
}) {
 | 
			
		||||
	params.Must.
 | 
			
		||||
		Field("newAPINodeHost", params.Host).
 | 
			
		||||
		Require("请输入新的API节点IP或域名").
 | 
			
		||||
		Field("newAPINodePort", params.Port).
 | 
			
		||||
		Require("请输入新的API节点端口")
 | 
			
		||||
 | 
			
		||||
	if !regexp.MustCompile(`^\d{1,5}$`).MatchString(params.Port) {
 | 
			
		||||
		this.FailField("newAPINodePort", "请输入正确的端口")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查端口
 | 
			
		||||
	config, err := configs.LoadAPIConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("加载当前平台的API配置失败:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	config.RPC.Endpoints = []string{params.Protocol + "://" + configutils.QuoteIP(params.Host) + ":" + params.Port}
 | 
			
		||||
	client, err := rpc.NewRPCClient(config, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.Fail("检查API节点地址出错:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		_ = client.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	_, err = client.AdminRPC().FindAdminFullname(this.AdminContext(), &pb.FindAdminFullnameRequest{AdminId: this.AdminId()})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		statusErr, ok := status.FromError(err)
 | 
			
		||||
		if ok {
 | 
			
		||||
			if statusErr.Code() == codes.Unavailable {
 | 
			
		||||
				this.Fail("测试新API节点失败:无法连接新的API节点:请检查:1、API节点地址和端口是否正确;2、防火墙或安全策略是否已正确设置。详细原因:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		this.Fail("测试新API节点失败:" + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 所有API节点
 | 
			
		||||
	apiNodesResp, err := client.APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.ErrorPage(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var apiNodes = apiNodesResp.ApiNodes
 | 
			
		||||
	var hosts = []string{}
 | 
			
		||||
	for _, node := range apiNodes {
 | 
			
		||||
		if !node.IsOn {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// http
 | 
			
		||||
		if len(node.HttpJSON) > 0 {
 | 
			
		||||
			var config = &serverconfigs.HTTPProtocolConfig{}
 | 
			
		||||
			err = json.Unmarshal(node.HttpJSON, config)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点HTTP信息失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, listen := range config.Listen {
 | 
			
		||||
				if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) {
 | 
			
		||||
					hosts = append(hosts, listen.Host)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// https
 | 
			
		||||
		if len(node.HttpsJSON) > 0 {
 | 
			
		||||
			var config = &serverconfigs.HTTPSProtocolConfig{}
 | 
			
		||||
			err = json.Unmarshal(node.HttpsJSON, config)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点HTTPS信息失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, listen := range config.Listen {
 | 
			
		||||
				if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) {
 | 
			
		||||
					hosts = append(hosts, listen.Host)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// restHTTP
 | 
			
		||||
		if len(node.RestHTTPJSON) > 0 {
 | 
			
		||||
			var config = &serverconfigs.HTTPProtocolConfig{}
 | 
			
		||||
			err = json.Unmarshal(node.RestHTTPJSON, config)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点REST HTTP信息失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, listen := range config.Listen {
 | 
			
		||||
				if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) {
 | 
			
		||||
					hosts = append(hosts, listen.Host)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// restHTTPS
 | 
			
		||||
		if len(node.RestHTTPSJSON) > 0 {
 | 
			
		||||
			var config = &serverconfigs.HTTPSProtocolConfig{}
 | 
			
		||||
			err = json.Unmarshal(node.RestHTTPSJSON, config)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点REST HTTPS信息失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, listen := range config.Listen {
 | 
			
		||||
				if len(listen.Host) > 0 && !lists.ContainsString(hosts, listen.Host) {
 | 
			
		||||
					hosts = append(hosts, listen.Host)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// access addrs
 | 
			
		||||
		if len(node.AccessAddrsJSON) > 0 {
 | 
			
		||||
			var addrs []*serverconfigs.NetworkAddressConfig
 | 
			
		||||
			err = json.Unmarshal(node.AccessAddrsJSON, &addrs)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				this.Fail("读取节点访问地址失败:" + err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			for _, addr := range addrs {
 | 
			
		||||
				if len(addr.Host) > 0 && !lists.ContainsString(hosts, addr.Host) {
 | 
			
		||||
					hosts = append(hosts, addr.Host)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	this.Data["hosts"] = hosts
 | 
			
		||||
 | 
			
		||||
	this.Success()
 | 
			
		||||
}
 | 
			
		||||
@@ -117,6 +117,7 @@ import (
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/profile"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/security"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/transfer"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ui"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/upgrade"
 | 
			
		||||
	_ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui"
 | 
			
		||||
 
 | 
			
		||||
@@ -2306,7 +2306,7 @@ window.Tea.Action = function (action, params) {
 | 
			
		||||
				console.log(error);
 | 
			
		||||
 | 
			
		||||
				if (typeof (_errorFn) === "function") {
 | 
			
		||||
					_errorFn.call(Tea.Vue, {});
 | 
			
		||||
					_errorFn.call(Tea.Vue, { message: error.message });
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.then(function () {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								web/views/@default/settings/transfer/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								web/views/@default/settings/transfer/index.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
.steps .step.active .content {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
.step-box .button-group {
 | 
			
		||||
  margin-top: 1em;
 | 
			
		||||
}
 | 
			
		||||
.step-box .button-group .next {
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
.step-box .content {
 | 
			
		||||
  min-height: 6em;
 | 
			
		||||
}
 | 
			
		||||
/*# sourceMappingURL=index.css.map */
 | 
			
		||||
							
								
								
									
										1
									
								
								web/views/@default/settings/transfer/index.css.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								web/views/@default/settings/transfer/index.css.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,MACC,MAAK,OACJ;EACC,iBAAA;;AAKH,SACC;EACC,eAAA;;AAFF,SACC,cAGC;EACC,YAAA;;AALH,SASC;EACC,eAAA","file":"index.css"}
 | 
			
		||||
							
								
								
									
										304
									
								
								web/views/@default/settings/transfer/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								web/views/@default/settings/transfer/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,304 @@
 | 
			
		||||
{$layout}
 | 
			
		||||
 | 
			
		||||
<div class="margin"></div>
 | 
			
		||||
 | 
			
		||||
<div class="ui steps fluid small">
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_PREPARE}">
 | 
			
		||||
        <div class="content">开始</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_DATABASE}">
 | 
			
		||||
        <div class="content">迁移数据库</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_API}">
 | 
			
		||||
        <div class="content">迁移API节点</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_ADDRESS}">
 | 
			
		||||
        <div class="content">变更地址</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_ADMIN}">
 | 
			
		||||
        <div class="content">迁移管理平台</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_UPGRADE}">
 | 
			
		||||
        <div class="content">升级节点配置</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui step" :class="{active: step == STEP_FINISH}">
 | 
			
		||||
        <div class="content">完成</div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 准备工作 -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_PREPARE">
 | 
			
		||||
    <div class="content">
 | 
			
		||||
        <p>通过此引导程序可以帮助你将当前平台配置迁移到新的平台中。</p>
 | 
			
		||||
        <p>每个步骤请小心操作,一旦配置错误可能需要手工还原。</p>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="ui button-group">
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doPrepare()">下一步</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 数据库 -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_DATABASE">
 | 
			
		||||
    <form class="ui form">
 | 
			
		||||
        <table class="ui table selectable definition">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td class="title">数据库地址是否改变?</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                   <div class="ui fields inline">
 | 
			
		||||
                       <div class="ui field">
 | 
			
		||||
                           <div class="ui radio checkbox">
 | 
			
		||||
                               <input type="radio" value="0" v-model="databaseChanged" id="database-changed-0" class="hidden"/>
 | 
			
		||||
                               <label for="database-changed-0">没有变</label>
 | 
			
		||||
                           </div>
 | 
			
		||||
                       </div>
 | 
			
		||||
                       <div class="ui field">
 | 
			
		||||
                           <div class="ui radio checkbox">
 | 
			
		||||
                               <input type="radio" value="1" v-model="databaseChanged" id="database-changed-1" class="hidden"/>
 | 
			
		||||
                               <label for="database-changed-1">变了</label>
 | 
			
		||||
                           </div>
 | 
			
		||||
                       </div>
 | 
			
		||||
                   </div>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr v-if="databaseChanged == 1">
 | 
			
		||||
                <td>数据是否已经导入到新的数据库?</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <div class="ui fields inline">
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="0" v-model="databaseTransferred" id="database-transferred-0" class="hidden"/>
 | 
			
		||||
                                <label for="database-transferred-0">没有导入</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="1" v-model="databaseTransferred" id="database-transferred-1" class="hidden"/>
 | 
			
		||||
                                <label for="database-transferred-1">已经导入</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <p class="comment" v-if="databaseTransferred == 0"><span class="red">请自行将老的数据库中数据导入到新的数据库中。</span></p>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </table>
 | 
			
		||||
    </form>
 | 
			
		||||
    <div class="ui button-group">
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_PREPARE)">上一步</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doDatabase()">下一步</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 检查API节点 -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_API">
 | 
			
		||||
    <form class="ui form">
 | 
			
		||||
        <table class="ui table selectable definition">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td class="title">API节点地址是否变更?</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <div class="ui fields inline">
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="1" v-model="apiNodeChanged" id="api-node-changed-1" class="hidden"/>
 | 
			
		||||
                                <label for="api-node-changed-1">已经变更</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="0" v-model="apiNodeChanged" id="api-node-changed-0" class="hidden"/>
 | 
			
		||||
                                <label for="api-node-changed-0">没有变更</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tbody v-show="apiNodeChanged == 1">
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>是否已安装新的API节点</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <div class="ui fields inline">
 | 
			
		||||
                            <div class="ui field">
 | 
			
		||||
                                <div class="ui radio checkbox">
 | 
			
		||||
                                    <input type="radio" value="1" v-model="apiNodeInstalled" id="api-node-installed-1" class="hidden"/>
 | 
			
		||||
                                    <label for="api-node-installed-1">已经安装</label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="ui field">
 | 
			
		||||
                                <div class="ui radio checkbox">
 | 
			
		||||
                                    <input type="radio" value="0" v-model="apiNodeInstalled" id="api-node-installed-0" class="hidden"/>
 | 
			
		||||
                                    <label for="api-node-installed-0">没有安装</label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <p class="comment" v-if="apiNodeInstalled == 0"><span class="red">请先安装新的API节点,请拷贝当前的API节点配置<code-label>configs/api.yaml</code-label>和<code-label>configs/db.yaml</code-label>到新API节点对应的位置,并记得修改其中的<code-label>configs/db.yaml</code-label>中的数据库地址、用户名、密码。</span></p>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>新的API节点IP或域名 *</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <input type="text" name="newAPINodeHost" placeholder="IP或域名" v-model="apiNodeHost" style="width: 14em" maxlength="100"/>
 | 
			
		||||
                        <p class="comment">如果有多个IP或者域名,填写任意其中一个即可。</p>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>新的API节点端口 *</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <input type="text" name="newAPINodePort" v-model="apiNodePort" style="width: 5em" maxlength="5" placeholder="端口"/>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>新的API节点协议</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <select class="ui dropdown auto-width" v-model="apiNodeProtocol">
 | 
			
		||||
                            <option value="http">HTTP</option>
 | 
			
		||||
                            <option value="https">HTTPS</option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
    <div class="button-group">
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_DATABASE)">上一步</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doAPI()">下一步</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 地址 -->
 | 
			
		||||
<div class="step-box" v-show="step == STEP_ADDRESS">
 | 
			
		||||
    <form method="post" class="ui form" data-tea-action=".updateHosts" data-tea-success="doAddress">
 | 
			
		||||
        <input type="hidden" name="host" :value="apiNodeHost"/>
 | 
			
		||||
        <input type="hidden" name="port" :value="apiNodePort"/>
 | 
			
		||||
        <input type="hidden" name="protocol" :value="apiNodeProtocol"/>
 | 
			
		||||
        <table class="ui table selectable celled">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th style="width: 50%">API节点原地址</th>
 | 
			
		||||
                <th>API节点新地址(留空表示不修改)</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tr v-for="host in apiAddressHosts">
 | 
			
		||||
                <td>{{host}}</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <input type="text" maxlength="100" name="newHosts"/>
 | 
			
		||||
                    <input type="hidden" name="oldHosts" :value="host"/>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </table>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <div class="button-group">
 | 
			
		||||
            <button class="ui button prev" @click.prevent="doBack(STEP_API)">上一步</button>
 | 
			
		||||
            <button type="submit" class="ui button primary next">下一步</button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- admin -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_ADMIN">
 | 
			
		||||
    <form class="ui form">
 | 
			
		||||
        <table class="ui table selectable definition">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td class="title">管理平台地址是否变更?</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <div class="ui fields inline">
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="1" v-model="adminNodeChanged" id="admin-node-changed-1" class="hidden"/>
 | 
			
		||||
                                <label for="admin-node-changed-1">已经变更</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="ui field">
 | 
			
		||||
                            <div class="ui radio checkbox">
 | 
			
		||||
                                <input type="radio" value="0" v-model="adminNodeChanged" id="admin-node-changed-0" class="hidden"/>
 | 
			
		||||
                                <label for="admin-node-changed-0">没有变更</label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tbody v-show="adminNodeChanged == 1">
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>是否已安装新的管理平台</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        <div class="ui fields inline">
 | 
			
		||||
                            <div class="ui field">
 | 
			
		||||
                                <div class="ui radio checkbox">
 | 
			
		||||
                                    <input type="radio" value="1" v-model="adminNodeInstalled" id="admin-node-installed-1" class="hidden"/>
 | 
			
		||||
                                    <label for="admin-node-installed-1">已经安装</label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="ui field">
 | 
			
		||||
                                <div class="ui radio checkbox">
 | 
			
		||||
                                    <input type="radio" value="0" v-model="adminNodeInstalled" id="admin-node-installed-0" class="hidden"/>
 | 
			
		||||
                                    <label for="admin-node-installed-0">没有安装</label>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <p class="comment" v-if="adminNodeInstalled == 0"><span class="red">请先安装新的管理平台,请拷贝当前的管理平台下的<code-label>configs/</code-label>目录下的配置到新管理平台对应的位置。</span></p>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
    <div class="ui button-group">
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_API)" v-if="apiNodeChanged == 0">上一步</button>
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_ADDRESS)" v-if="apiNodeChanged == 1">上一步</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doAdmin()">下一步</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 升级节点配置 -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_UPGRADE">
 | 
			
		||||
    <form class="ui form">
 | 
			
		||||
        <table class="ui table selectable definition">
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td class="title">升级节点API地址配置</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <span>{{apiNodeProtocol}}://<span v-if="apiNodeHost.indexOf(':')>0">[{{apiNodeHost}}]</span><span v-else>{{apiNodeHost}}</span>:{{apiNodePort}}</span>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>当前平台节点数</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <span v-if="isUpgrading">{{countNodes}}</span>
 | 
			
		||||
                    <span v-else>-</span>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>新平台节点数</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <span v-if="isUpgrading">{{countFinishedNodes}}</span>
 | 
			
		||||
                    <span v-else>-</span>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>完成比例</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <span v-if="isUpgrading">{{percentNodes}}%</span>
 | 
			
		||||
                    <span v-else>-</span>
 | 
			
		||||
                </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </table>
 | 
			
		||||
    </form>
 | 
			
		||||
    <div class="ui button-group">
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_ADMIN)">上一步</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doStartUpgrade()" v-if="!isUpgrading">开始升级</button>
 | 
			
		||||
        <button class="ui button next" :class="{disabled: percentNodes<100}" v-if="isUpgrading && percentNodes < 100">等待完成</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doUpgrade()" v-if="isUpgrading && percentNodes == 100">下一步</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<!-- 完成 -->
 | 
			
		||||
<div class="step-box" v-if="step == STEP_FINISH">
 | 
			
		||||
    <div class="content">
 | 
			
		||||
        <p v-if="adminNodeChanged == 1">所有迁移任务已完成,请登录新的管理平台进行管理。</p>
 | 
			
		||||
        <p v-if="adminNodeChanged == 0">所有迁移任务已完成。</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="ui button-group">
 | 
			
		||||
        <button class="ui button prev" @click.prevent="doBack(STEP_UPGRADE)">上一步</button>
 | 
			
		||||
        <button class="ui button primary next" @click.prevent="doFinish()">完成</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										191
									
								
								web/views/@default/settings/transfer/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								web/views/@default/settings/transfer/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
Tea.context(function () {
 | 
			
		||||
	this.STEP_PREPARE = "prepare"
 | 
			
		||||
	this.STEP_DATABASE = "database"
 | 
			
		||||
	this.STEP_ADMIN = "admin"
 | 
			
		||||
	this.STEP_API = "api"
 | 
			
		||||
	this.STEP_ADDRESS = "address"
 | 
			
		||||
	this.STEP_UPGRADE = "upgrade"
 | 
			
		||||
	this.STEP_FINISH = "finish"
 | 
			
		||||
 | 
			
		||||
	this.step = this.STEP_PREPARE
 | 
			
		||||
 | 
			
		||||
	this.doBack = function (step) {
 | 
			
		||||
		this.step = step
 | 
			
		||||
 | 
			
		||||
		switch (step) {
 | 
			
		||||
			case this.STEP_UPGRADE:
 | 
			
		||||
				if (this.apiNodeChanged == 0) {
 | 
			
		||||
					this.doBack(this.STEP_ADMIN)
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 准备工作
 | 
			
		||||
	 */
 | 
			
		||||
	this.doPrepare = function () {
 | 
			
		||||
		this.step = this.STEP_DATABASE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 数据库
 | 
			
		||||
	 */
 | 
			
		||||
	this.databaseChanged = 1
 | 
			
		||||
	this.databaseTransferred = 0
 | 
			
		||||
 | 
			
		||||
	this.doDatabase = function () {
 | 
			
		||||
		if (this.databaseChanged == 1 && this.databaseTransferred == 0) {
 | 
			
		||||
			teaweb.warn("请先将老的数据导入到新的数据库中。")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.step = this.STEP_API
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * API
 | 
			
		||||
	 */
 | 
			
		||||
	this.apiNodeChanged = 1
 | 
			
		||||
	this.apiNodeHost = ""
 | 
			
		||||
	this.apiNodePort = ""
 | 
			
		||||
	this.apiNodeProtocol = "http"
 | 
			
		||||
	this.apiNodeInstalled = 1
 | 
			
		||||
 | 
			
		||||
	this.doAPI = function () {
 | 
			
		||||
		if (this.apiNodeChanged == 0) {
 | 
			
		||||
			this.step = this.STEP_ADMIN
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if (this.apiNodeInstalled == 0) {
 | 
			
		||||
			teaweb.warn("请先安装新的API节点")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.$post(".validateAPI")
 | 
			
		||||
			.params({
 | 
			
		||||
				host: this.apiNodeHost,
 | 
			
		||||
				port: this.apiNodePort,
 | 
			
		||||
				protocol: this.apiNodeProtocol
 | 
			
		||||
			})
 | 
			
		||||
			.timeout(30)
 | 
			
		||||
			.success(function (resp) {
 | 
			
		||||
				if (this.apiNodeChanged == 1) {
 | 
			
		||||
					this.step = this.STEP_ADDRESS
 | 
			
		||||
					this.apiAddressHosts = resp.data.hosts
 | 
			
		||||
				} else {
 | 
			
		||||
					this.step = this.STEP_ADMIN
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 修改地址
 | 
			
		||||
	 */
 | 
			
		||||
	this.apiAddresses = []
 | 
			
		||||
	this.apiAddressHosts = []
 | 
			
		||||
 | 
			
		||||
	this.doAddress = function () {
 | 
			
		||||
		this.step = this.STEP_ADMIN
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 管理平台
 | 
			
		||||
	 */
 | 
			
		||||
	this.adminNodeChanged = 1
 | 
			
		||||
	this.adminNodeInstalled = 1
 | 
			
		||||
 | 
			
		||||
	this.doAdmin = function () {
 | 
			
		||||
		if (this.adminNodeChanged == 1 && this.adminNodeInstalled == 0) {
 | 
			
		||||
			teaweb.warn("请先安装新的管理平台")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (this.apiNodeChanged == 0) {
 | 
			
		||||
			this.step = this.STEP_FINISH
 | 
			
		||||
		} else {
 | 
			
		||||
			this.step = this.STEP_UPGRADE
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 边缘节点
 | 
			
		||||
	 */
 | 
			
		||||
	this.isUpgrading = false
 | 
			
		||||
	this.percentNodes = 0
 | 
			
		||||
	this.countNodes = 0
 | 
			
		||||
	this.countFinishedNodes = 0
 | 
			
		||||
 | 
			
		||||
	this.doStartUpgrade = function () {
 | 
			
		||||
		this.percentNodes = 0
 | 
			
		||||
		this.countNodes = 0
 | 
			
		||||
		this.countFinishedNodes = 0
 | 
			
		||||
 | 
			
		||||
		this.$post(".statNodes")
 | 
			
		||||
			.success(function (resp) {
 | 
			
		||||
				this.countNodes = resp.data.countNodes
 | 
			
		||||
				if (this.countNodes == 0) {
 | 
			
		||||
					this.isUpgrading = true
 | 
			
		||||
					this.percentNodes = 100
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				this.isUpgrading = true
 | 
			
		||||
				this.upgradeNodeTimer()
 | 
			
		||||
			})
 | 
			
		||||
			.fail(function () {
 | 
			
		||||
 | 
			
		||||
			})
 | 
			
		||||
			.error(function () {
 | 
			
		||||
 | 
			
		||||
			})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.upgradeNodeTimer = function () {
 | 
			
		||||
		if (!this.isUpgrading) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if (this.percentNodes == 100) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		this.$post(".upgradeNodes")
 | 
			
		||||
			.params({
 | 
			
		||||
				apiNodeProtocol: this.apiNodeProtocol,
 | 
			
		||||
				apiNodeHost: this.apiNodeHost,
 | 
			
		||||
				apiNodePort: this.apiNodePort
 | 
			
		||||
			})
 | 
			
		||||
			.success(function (resp) {
 | 
			
		||||
				this.countFinishedNodes += resp.data.count
 | 
			
		||||
				if (this.countNodes > 0) {
 | 
			
		||||
					this.percentNodes = this.countFinishedNodes * 100 / this.countNodes
 | 
			
		||||
					if (this.percentNodes > 100) {
 | 
			
		||||
						this.percentNodes = 100
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (resp.data.hasNext) {
 | 
			
		||||
					this.$delay(function () {
 | 
			
		||||
						this.upgradeNodeTimer()
 | 
			
		||||
					}, 5000)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.fail(function (resp) {
 | 
			
		||||
				this.isUpgrading = false
 | 
			
		||||
 | 
			
		||||
				teaweb.warn(resp.message)
 | 
			
		||||
			})
 | 
			
		||||
			.error(function (err) {
 | 
			
		||||
				teaweb.warn("请求错误:" + err.message)
 | 
			
		||||
				this.isUpgrading = false
 | 
			
		||||
			})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.doUpgrade = function () {
 | 
			
		||||
		this.step = this.STEP_FINISH
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 完成
 | 
			
		||||
	 */
 | 
			
		||||
	this.doFinish = function () {
 | 
			
		||||
		window.location = "/"
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										21
									
								
								web/views/@default/settings/transfer/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/views/@default/settings/transfer/index.less
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
.steps {
 | 
			
		||||
	.step.active {
 | 
			
		||||
		.content {
 | 
			
		||||
			font-weight: bold;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.step-box {
 | 
			
		||||
	.button-group {
 | 
			
		||||
		margin-top: 1em;
 | 
			
		||||
 | 
			
		||||
		.next {
 | 
			
		||||
			float: right;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.content {
 | 
			
		||||
		min-height: 6em;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user