mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-11 10:10:28 +08:00
实现API节点远程升级
This commit is contained in:
30
internal/utils/apinodeutils/manager.go
Normal file
30
internal/utils/apinodeutils/manager.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
var SharedManager = NewManager()
|
||||
|
||||
type Manager struct {
|
||||
upgraderMap map[int64]*Upgrader
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
upgraderMap: map[int64]*Upgrader{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Manager) AddUpgrader(upgrader *Upgrader) {
|
||||
this.upgraderMap[upgrader.apiNodeId] = upgrader
|
||||
}
|
||||
|
||||
func (this *Manager) FindUpgrader(apiNodeId int64) *Upgrader {
|
||||
return this.upgraderMap[apiNodeId]
|
||||
}
|
||||
|
||||
func (this *Manager) RemoveUpgrader(upgrader *Upgrader) {
|
||||
if upgrader == nil {
|
||||
return
|
||||
}
|
||||
delete(this.upgraderMap, upgrader.apiNodeId)
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
@@ -16,9 +15,7 @@ import (
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
@@ -29,47 +26,29 @@ type Progress struct {
|
||||
type Upgrader struct {
|
||||
progress *Progress
|
||||
apiExe string
|
||||
apiNodeId int64
|
||||
}
|
||||
|
||||
func NewUpgrader() *Upgrader {
|
||||
func NewUpgrader(apiNodeId int64) *Upgrader {
|
||||
return &Upgrader{
|
||||
apiExe: Tea.Root + "/edge-api/bin/edge-api",
|
||||
apiExe: apiExe(),
|
||||
progress: &Progress{Percent: 0},
|
||||
apiNodeId: apiNodeId,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Upgrader) CanUpgrade(apiVersion string) (canUpgrade bool, reason string) {
|
||||
stat, err := os.Stat(this.apiExe)
|
||||
if err != nil {
|
||||
return false, "stat error: " + err.Error()
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return false, "is directory"
|
||||
}
|
||||
|
||||
localVersion, err := this.localVersion()
|
||||
if err != nil {
|
||||
return false, "lookup version failed: " + err.Error()
|
||||
}
|
||||
if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
|
||||
return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (this *Upgrader) Upgrade(apiNodeId int64) error {
|
||||
func (this *Upgrader) Upgrade() error {
|
||||
sharedClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
|
||||
apiNodeResp, err := sharedClient.APINodeRPC().FindEnabledAPINode(sharedClient.Context(0), &pb.FindEnabledAPINodeRequest{ApiNodeId: this.apiNodeId})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var apiNode = apiNodeResp.ApiNode
|
||||
if apiNode == nil {
|
||||
return errors.New("could not find api node with id '" + types.String(apiNodeId) + "'")
|
||||
return errors.New("could not find api node with id '" + types.String(this.apiNodeId) + "'")
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
@@ -93,12 +72,12 @@ func (this *Upgrader) Upgrade(apiNodeId int64) error {
|
||||
}
|
||||
|
||||
// 检查本地文件版本
|
||||
canUpgrade, reason := this.CanUpgrade(versionResp.Version)
|
||||
canUpgrade, reason := CanUpgrade(versionResp.Version, versionResp.Os, versionResp.Arch)
|
||||
if !canUpgrade {
|
||||
return errors.New(reason)
|
||||
}
|
||||
|
||||
localVersion, err := this.localVersion()
|
||||
localVersion, err := localVersion()
|
||||
if err != nil {
|
||||
return errors.New("lookup version failed: " + err.Error())
|
||||
}
|
||||
@@ -220,22 +199,3 @@ func (this *Upgrader) Upgrade(apiNodeId int64) error {
|
||||
func (this *Upgrader) Progress() *Progress {
|
||||
return this.progress
|
||||
}
|
||||
|
||||
func (this *Upgrader) localVersion() (string, error) {
|
||||
var cmd = exec.Command(this.apiExe, "-V")
|
||||
var output = &bytes.Buffer{}
|
||||
cmd.Stdout = output
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var localVersion = output.String()
|
||||
|
||||
// 检查版本号
|
||||
var reg = regexp.MustCompile(`^[\d.]+$`)
|
||||
if !reg.MatchString(localVersion) {
|
||||
return "", errors.New("lookup version failed: " + localVersion)
|
||||
}
|
||||
|
||||
return localVersion, nil
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ package apinodeutils_test
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUpgrader_CanUpgrade(t *testing.T) {
|
||||
var upgrader = apinodeutils.NewUpgrader()
|
||||
t.Log(upgrader.CanUpgrade("0.6.3"))
|
||||
t.Log(apinodeutils.CanUpgrade("0.6.3", runtime.GOOS, runtime.GOARCH))
|
||||
}
|
||||
|
||||
func TestUpgrader_Upgrade(t *testing.T) {
|
||||
var upgrader = apinodeutils.NewUpgrader()
|
||||
err := upgrader.Upgrade(1)
|
||||
var upgrader = apinodeutils.NewUpgrader(1)
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
76
internal/utils/apinodeutils/utils.go
Normal file
76
internal/utils/apinodeutils/utils.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package apinodeutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CanUpgrade(apiVersion string, osName string, arch string) (canUpgrade bool, reason string) {
|
||||
if len(apiVersion) == 0 {
|
||||
return false, "current api version should not be empty"
|
||||
}
|
||||
|
||||
if osName != runtime.GOOS {
|
||||
return false, "os not match: " + osName
|
||||
}
|
||||
if arch != runtime.GOARCH {
|
||||
return false, "arch not match: " + arch
|
||||
}
|
||||
|
||||
stat, err := os.Stat(apiExe())
|
||||
if err != nil {
|
||||
return false, "stat error: " + err.Error()
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return false, "is directory"
|
||||
}
|
||||
|
||||
localVersion, err := localVersion()
|
||||
if err != nil {
|
||||
return false, "lookup version failed: " + err.Error()
|
||||
}
|
||||
if localVersion != teaconst.APINodeVersion {
|
||||
return false, "not newest api node"
|
||||
}
|
||||
if stringutil.VersionCompare(localVersion, apiVersion) <= 0 {
|
||||
return false, "need not upgrade, local '" + localVersion + "' vs remote '" + apiVersion + "'"
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
func localVersion() (string, error) {
|
||||
var cmd = exec.Command(apiExe(), "-V")
|
||||
var output = &bytes.Buffer{}
|
||||
cmd.Stdout = output
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var localVersion = strings.TrimSpace(output.String())
|
||||
|
||||
// 检查版本号
|
||||
var reg = regexp.MustCompile(`^[\d.]+$`)
|
||||
if !reg.MatchString(localVersion) {
|
||||
return "", errors.New("lookup version failed: " + localVersion)
|
||||
}
|
||||
|
||||
return localVersion, nil
|
||||
}
|
||||
|
||||
|
||||
func apiExe() string {
|
||||
return Tea.Root + "/edge-api/bin/edge-api"
|
||||
}
|
||||
@@ -3,12 +3,15 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"time"
|
||||
)
|
||||
@@ -44,7 +47,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
|
||||
for _, node := range nodesResp.ApiNodes {
|
||||
// 状态
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
@@ -55,7 +58,7 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
}
|
||||
|
||||
// Rest地址
|
||||
restAccessAddrs := []string{}
|
||||
var restAccessAddrs = []string{}
|
||||
if node.RestIsOn {
|
||||
if len(node.RestHTTPJSON) > 0 {
|
||||
httpConfig := &serverconfigs.HTTPProtocolConfig{}
|
||||
@@ -86,6 +89,9 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpgrade = status.IsActive && len(status.BuildVersion) > 0 && stringutil.VersionCompare(teaconst.APINodeVersion, status.BuildVersion) > 0
|
||||
canUpgrade, _ := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
|
||||
nodeMaps = append(nodeMaps, maps.Map{
|
||||
"id": node.Id,
|
||||
"isOn": node.IsOn,
|
||||
@@ -102,6 +108,9 @@ func (this *IndexAction) RunGet(params struct{}) {
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"buildVersion": status.BuildVersion,
|
||||
"latestVersion": teaconst.APINodeVersion,
|
||||
"shouldUpgrade": shouldUpgrade,
|
||||
"canUpgrade": shouldUpgrade && canUpgrade,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ func init() {
|
||||
GetPost("/update", new(UpdateAction)).
|
||||
Get("/install", new(InstallAction)).
|
||||
Get("/logs", new(LogsAction)).
|
||||
GetPost("/upgradePopup", new(UpgradePopupAction)).
|
||||
Post("/upgradeCheck", new(UpgradeCheckAction)).
|
||||
|
||||
//
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
67
internal/web/actions/default/api/node/upgradeCheck.go
Normal file
67
internal/web/actions/default/api/node/upgradeCheck.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
// UpgradeCheckAction 检查升级结果
|
||||
type UpgradeCheckAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradeCheckAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["isOk"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil || len(node.AccessAddrs) == 0 {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
apiConfig, err := configs.LoadAPIConfig()
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var newAPIConfig = apiConfig.Clone()
|
||||
newAPIConfig.RPC.Endpoints = node.AccessAddrs
|
||||
rpcClient, err := rpc.NewRPCClient(newAPIConfig, false)
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
versionResp, err := rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(0), &pb.FindCurrentAPINodeVersionRequest{})
|
||||
if err != nil {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
if versionResp.Version != teaconst.Version {
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["isOk"] = true
|
||||
|
||||
this.Success()
|
||||
}
|
||||
124
internal/web/actions/default/api/node/upgradePopup.go
Normal file
124
internal/web/actions/default/api/node/upgradePopup.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/apinodeutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpgradePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["nodeName"] = ""
|
||||
this.Data["currentVersion"] = ""
|
||||
this.Data["latestVersion"] = ""
|
||||
this.Data["result"] = ""
|
||||
this.Data["resultIsOk"] = true
|
||||
this.Data["canUpgrade"] = false
|
||||
this.Data["isUpgrading"] = false
|
||||
|
||||
nodeResp, err := this.RPC().APINodeRPC().FindEnabledAPINode(this.AdminContext(), &pb.FindEnabledAPINodeRequest{ApiNodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var node = nodeResp.ApiNode
|
||||
if node == nil {
|
||||
this.Data["result"] = "要升级的节点不存在"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["nodeName"] = node.Name + " / [" + strings.Join(node.AccessAddrs, ", ") + "]"
|
||||
|
||||
// 节点状态
|
||||
var status = &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
this.ErrorPage(errors.New("decode status failed: " + err.Error()))
|
||||
return
|
||||
}
|
||||
this.Data["currentVersion"] = status.BuildVersion
|
||||
} else {
|
||||
this.Data["result"] = "无法检测到节点当前版本"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["latestVersion"] = teaconst.APINodeVersion
|
||||
|
||||
if status.IsActive && len(status.BuildVersion) > 0 {
|
||||
canUpgrade, reason := apinodeutils.CanUpgrade(status.BuildVersion, status.OS, status.Arch)
|
||||
if !canUpgrade {
|
||||
this.Data["result"] = reason
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
this.Data["canUpgrade"] = true
|
||||
this.Data["result"] = "等待升级"
|
||||
this.Data["resultIsOk"] = true
|
||||
} else {
|
||||
this.Data["result"] = "当前节点非连接状态无法远程升级"
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Show()
|
||||
return
|
||||
}
|
||||
|
||||
// 是否正在升级
|
||||
var oldUpgrader = apinodeutils.SharedManager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Data["result"] = "正在升级中..."
|
||||
this.Data["resultIsOk"] = false
|
||||
this.Data["isUpgrading"] = true
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpgradePopupAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
|
||||
Must *actions.Must
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
var manager = apinodeutils.SharedManager
|
||||
|
||||
var oldUpgrader = manager.FindUpgrader(params.NodeId)
|
||||
if oldUpgrader != nil {
|
||||
this.Fail("正在升级中,无需重复提交 ...")
|
||||
return
|
||||
}
|
||||
|
||||
var upgrader = apinodeutils.NewUpgrader(params.NodeId)
|
||||
manager.AddUpgrader(upgrader)
|
||||
defer func() {
|
||||
manager.RemoveUpgrader(upgrader)
|
||||
}()
|
||||
|
||||
err := upgrader.Upgrade()
|
||||
if err != nil {
|
||||
this.Fail("升级失败:" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
@@ -23,7 +23,12 @@
|
||||
</thead>
|
||||
<tr v-for="node in nodes">
|
||||
<td><a :href="'/api/node?nodeId=' + node.id">{{node.name}}</a>
|
||||
<div v-if="node.isPrimary">
|
||||
<grey-label v-if="node.isPrimary">主节点</grey-label>
|
||||
</div>
|
||||
<div v-if="node.status != null && node.status.shouldUpgrade">
|
||||
<span class="red small">v{{node.status.buildVersion}} -> v{{node.status.latestVersion}}<br/><a href="" v-if="node.status.canUpgrade" @click.prevent="upgradeNode(node.id)">[远程升级]</a> </span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="node.accessAddrs != null && node.accessAddrs.length > 0">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Tea.context(function () {
|
||||
// 创建节点
|
||||
this.createNode = function () {
|
||||
teaweb.popup("/api/node/createPopup", {
|
||||
teaweb.popup(".node.createPopup", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function () {
|
||||
@@ -16,11 +16,20 @@ Tea.context(function () {
|
||||
this.deleteNode = function (nodeId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除此节点吗?", function () {
|
||||
that.$post("/api/delete")
|
||||
that.$post(".delete")
|
||||
.params({
|
||||
nodeId: nodeId
|
||||
})
|
||||
.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
// 升级节点
|
||||
this.upgradeNode = function (nodeId) {
|
||||
teaweb.popup(".node.upgradePopup?nodeId=" + nodeId, {
|
||||
onClose: function () {
|
||||
teaweb.reload()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
35
web/views/@default/api/node/upgradePopup.html
Normal file
35
web/views/@default/api/node/upgradePopup.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>远程升级</h3>
|
||||
|
||||
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-timeout="3600" data-tea-before="before" data-tea-done="done">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="nodeId" :value="nodeId"/>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr v-show="nodeName.length > 0">
|
||||
<td class="title">API节点</td>
|
||||
<td>{{nodeName}}</td>
|
||||
</tr>
|
||||
<tr v-show="currentVersion.length > 0">
|
||||
<td>当前版本</td>
|
||||
<td>v{{currentVersion}}</td>
|
||||
</tr>
|
||||
<tr v-show="latestVersion.length > 0">
|
||||
<td>目标版本</td>
|
||||
<td>v{{latestVersion}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">升级结果</td>
|
||||
<td>
|
||||
<span v-if="currentVersion == latestVersion" class="green">已经升级到最新版本</span>
|
||||
<span :class="{red: !resultIsOk}" v-if="currentVersion != latestVersion">
|
||||
<span v-if="!isRequesting">{{result}}</span>
|
||||
<span v-if="isRequesting">升级中...</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn v-show="canUpgrade && !isRequesting && !isUpgrading">开始升级</submit-btn>
|
||||
</form>
|
||||
39
web/views/@default/api/node/upgradePopup.js
Normal file
39
web/views/@default/api/node/upgradePopup.js
Normal file
@@ -0,0 +1,39 @@
|
||||
Tea.context(function () {
|
||||
this.$delay(function () {
|
||||
this.checkLoop()
|
||||
})
|
||||
|
||||
this.success = function () {
|
||||
}
|
||||
|
||||
this.isRequesting = false
|
||||
|
||||
this.before = function () {
|
||||
this.isRequesting = true
|
||||
}
|
||||
|
||||
this.done = function () {
|
||||
this.isRequesting = false
|
||||
}
|
||||
|
||||
this.checkLoop = function () {
|
||||
if (this.currentVersion == this.latestVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$post(".upgradeCheck")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
if (resp.data.isOk) {
|
||||
teaweb.reload()
|
||||
}
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.checkLoop()
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user