enhance: display downloading progress in upgrader

This commit is contained in:
0xJacky 2023-04-11 21:20:12 +08:00
parent defd81461e
commit 710325447a
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
7 changed files with 146 additions and 58 deletions

View file

@ -1 +1 @@
{"version":"1.7.9","build_id":88,"total_build":158}
{"version":"1.7.9","build_id":92,"total_build":162}

View file

@ -7,6 +7,7 @@ import dayjs from 'dayjs'
import {marked} from 'marked'
import Template from '@/views/template/Template.vue'
import websocket from '@/lib/websocket'
import {message} from 'ant-design-vue'
const {$gettext} = useGettext()
@ -20,15 +21,23 @@ const progressStrokeColor = {
}
const modalVisible = ref(false)
const progressPercent = ref(0)
const progressPercent: any = ref(0)
const progressStatus = ref('active')
const modalClosable = ref(false)
const get_release_error = ref(false)
function get_release_list() {
const progressPercentComputed = computed(() => {
return parseFloat(progressPercent.value.toFixed(1))
})
function get_latest_release() {
loading.value = true
upgrade.get_latest_release().then(r => {
data.value = r
last_check.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
}).catch(e => {
get_release_error.value = e?.message
message.error(e?.message ?? $gettext('Server error'))
}).finally(() => {
setTimeout(() => {
loading.value = false
@ -36,7 +45,7 @@ function get_release_list() {
})
}
get_release_list()
get_latest_release()
const is_latest_ver = computed(() => {
return data.value.name === `v${version.version}`
@ -64,14 +73,20 @@ async function perform_upgrade() {
const ws = websocket('/api/upgrade/perform', false)
ws.onmessage = m => {
const r = JSON.parse(m.data)
log(r.message)
let last = 0
ws.onmessage = async m => {
const r = JSON.parse(m.data)
if (r.message) log(r.message)
console.log(r.status)
switch (r.status) {
case 'info':
progressPercent.value += 10
break
case 'progress':
progressPercent.value += (r.progress - last) / 2
last = r.progress
break
case 'error':
progressStatus.value = 'exception'
modalClosable.value = true
@ -109,7 +124,7 @@ async function perform_upgrade() {
:footer="null" :closable="modalClosable" force-render>
<a-progress
:stroke-color="progressStrokeColor"
:percent="progressPercent"
:percent="progressPercentComputed"
:status="progressStatus"
/>
@ -118,32 +133,41 @@ async function perform_upgrade() {
<div class="upgrade-container">
<p>{{ $gettext('You can check Nginx UI upgrade at this page.') }}</p>
<h3>{{ $gettext('Current Version') }}: v{{ version.version }}</h3>
<p>{{ $gettext('OS') }}: {{ data.os }}</p>
<p>{{ $gettext('Arch') }}: {{ data.arch }}</p>
<p>{{ $gettext('Executable Path') }}: {{ data.ex_path }}</p>
<p>{{ $gettext('Last checked at') }}: {{ last_check }}
<a-button type="link" @click="get_release_list" :loading="loading">
{{ $gettext('Check again') }}
</a-button>
</p>
<a-alert type="success" v-if="is_latest_ver"
:message="$gettext('You are using the latest version')"
banner
/>
<a-alert type="info" v-else
:message="$gettext('New version released')"
banner
/>
<div class="control-btn">
<a-space>
<a-button type="primary" @click="perform_upgrade"
ghost v-if="is_latest_ver">{{ $gettext('Reinstall') }}
<template v-if="get_release_error">
<a-alert type="error"
:title="$gettext('Get release information error')"
:message="get_release_error"
banner
/>
</template>
<template v-else>
<p>{{ $gettext('OS') }}: {{ data.os }}</p>
<p>{{ $gettext('Arch') }}: {{ data.arch }}</p>
<p>{{ $gettext('Executable Path') }}: {{ data.ex_path }}</p>
<p>{{ $gettext('Last checked at') }}: {{ last_check }}
<a-button type="link" @click="get_latest_release" :loading="loading">
{{ $gettext('Check again') }}
</a-button>
<a-button type="primary" @click="perform_upgrade"
ghost v-else>{{ $gettext('Upgrade') }}
</a-button>
</a-space>
</div>
</p>
<a-alert type="success" v-if="is_latest_ver"
:message="$gettext('You are using the latest version')"
banner
/>
<a-alert type="info" v-else
:message="$gettext('New version released')"
banner
/>
<div class="control-btn">
<a-space>
<a-button type="primary" @click="perform_upgrade"
ghost v-if="is_latest_ver">{{ $gettext('Reinstall') }}
</a-button>
<a-button type="primary" @click="perform_upgrade"
ghost v-else>{{ $gettext('Upgrade') }}
</a-button>
</a-space>
</div>
</template>
<template v-if="data.body">
<h2>{{ $gettext('Release Note') }}</h2>
<div v-html="marked.parse(data.body)"></div>

View file

@ -1 +1 @@
{"version":"1.7.9","build_id":88,"total_build":158}
{"version":"1.7.9","build_id":92,"total_build":162}

1
go.mod
View file

@ -36,6 +36,7 @@ require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bytedance/sonic v1.8.7 // indirect
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect

2
go.sum
View file

@ -6,6 +6,8 @@ github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.7 h1:d3sry5vGgVq/OpgozRUNP6xBsSo0mtNdwliApw+SAMQ=
github.com/bytedance/sonic v1.8.7/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=

View file

@ -7,7 +7,6 @@ import (
"log"
"net/http"
"os"
"path/filepath"
)
func GetRelease(c *gin.Context) {
@ -67,7 +66,16 @@ func PerformCoreUpgrade(c *gin.Context) {
"status": "info",
"message": "Downloading latest release",
})
tarName, err := u.DownloadLatestRelease()
progressChan := make(chan float64)
go func() {
for progress := range progressChan {
_ = ws.WriteJSON(gin.H{
"status": "progress",
"progress": progress,
})
}
}()
tarName, err := u.DownloadLatestRelease(progressChan)
if err != nil {
_ = ws.WriteJSON(gin.H{
"status": "error",
@ -86,7 +94,7 @@ func PerformCoreUpgrade(c *gin.Context) {
})
_ = os.Remove(u.Release.ExPath)
// bye, overseer will restart nginx-ui
err = u.PerformCoreUpgrade(filepath.Dir(u.Release.ExPath), tarName)
err = u.PerformCoreUpgrade(u.Release.ExPath, tarName)
if err != nil {
_ = ws.WriteJSON(gin.H{
"status": "error",
@ -96,7 +104,7 @@ func PerformCoreUpgrade(c *gin.Context) {
"status": "error",
"message": err.Error(),
})
log.Println("[Error] PerformCoreUpgrade PerformCoreUpgrade", err)
log.Println("[Error] PerformCoreUpgrade", err)
return
}
}

View file

@ -9,11 +9,13 @@ import (
"github.com/0xJacky/Nginx-UI/server/settings"
"github.com/pkg/errors"
"io"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
)
@ -72,6 +74,12 @@ func GetRelease() (data TRelease, err error) {
err = errors.Wrap(err, "service.GetReleaseList io.ReadAll err")
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
err = errors.New(string(body))
log.Println(string(body))
return
}
err = json.Unmarshal(body, &data)
if err != nil {
err = errors.Wrap(err, "service.GetReleaseList json.Unmarshal err")
@ -118,7 +126,56 @@ func NewUpgrader() (u *Upgrader, err error) {
return
}
func (u *Upgrader) DownloadLatestRelease() (tarName string, err error) {
type ProgressWriter struct {
io.Writer
totalSize int64
currentSize int64
progressChan chan<- float64
}
func (pw *ProgressWriter) Write(p []byte) (int, error) {
n, err := pw.Writer.Write(p)
pw.currentSize += int64(n)
progress := float64(pw.currentSize) / float64(pw.totalSize) * 100
pw.progressChan <- progress
return n, err
}
func downloadRelease(url string, dir string, progressChan chan float64) (tarName string, err error) {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return
}
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
totalSize, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
if err != nil {
return
}
file, err := os.CreateTemp(dir, "nginx-ui-temp-*.tar.gz")
if err != nil {
err = errors.Wrap(err, "service.DownloadLatestRelease CreateTemp error")
return
}
defer file.Close()
progressWriter := &ProgressWriter{Writer: file, totalSize: totalSize, progressChan: progressChan}
multiWriter := io.MultiWriter(progressWriter)
_, err = io.Copy(multiWriter, resp.Body)
close(progressChan)
tarName = file.Name()
return
}
func (u *Upgrader) DownloadLatestRelease(progressChan chan float64) (tarName string, err error) {
bytes, err := _github.DistFS.ReadFile("build/build_info.json")
if err != nil {
err = errors.Wrap(err, "service.DownloadLatestRelease Read build_info.json error")
@ -156,12 +213,7 @@ func (u *Upgrader) DownloadLatestRelease() (tarName string, err error) {
}
dir := filepath.Dir(u.Release.ExPath)
file, err := os.CreateTemp(dir, "nginx-ui-temp-*.tar.gz")
if err != nil {
err = errors.Wrap(err, "service.DownloadLatestRelease CreateTemp error")
return
}
defer file.Close()
if settings.ServerSettings.GithubProxy != "" {
downloadUrl, err = url.JoinPath(settings.ServerSettings.GithubProxy, downloadUrl)
if err != nil {
@ -169,30 +221,31 @@ func (u *Upgrader) DownloadLatestRelease() (tarName string, err error) {
return
}
}
client := &http.Client{}
resp, err := client.Get(downloadUrl)
tarName, err = downloadRelease(downloadUrl, dir, progressChan)
if err != nil {
err = errors.Wrap(err, "service.DownloadLatestRelease client.Get() error")
err = errors.Wrap(err, "service.DownloadLatestRelease downloadFile error")
return
}
defer resp.Body.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
err = errors.Wrap(err, "service.DownloadLatestRelease io.Copy error")
return
}
tarName = file.Name()
return
}
func (u *Upgrader) PerformCoreUpgrade(dir, tarPath string) (err error) {
func (u *Upgrader) PerformCoreUpgrade(exPath string, tarPath string) (err error) {
dir := filepath.Dir(exPath)
err = helper.UnTar(dir, tarPath)
if err != nil {
err = errors.Wrap(err, "PerformCoreUpgrade unTar error")
return
}
err = os.Rename(filepath.Join(dir, "nginx-ui"), exPath)
if err != nil {
err = errors.Wrap(err, "PerformCoreUpgrade rename error")
return
}
_ = os.Remove(tarPath)
err = os.Remove(tarPath)
if err != nil {
err = errors.Wrap(err, "PerformCoreUpgrade remove tar error")
return
}
return
}