实现迁移辅助功能(系统设置 -- 高级设置 -- 迁移)

This commit is contained in:
GoEdgeLab
2021-11-20 18:58:58 +08:00
parent abda31c89a
commit 615e47e9d0
14 changed files with 971 additions and 1 deletions

View File

@@ -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")
}

View 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()
}

View 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()
})
}

View 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()
}

View 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()
}

View File

@@ -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()
}

View 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()
}

View File

@@ -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"