Finish button of BS update
This commit is contained in:
parent
6bfe30b394
commit
43051bad90
113
resources/assets/src/components/admin/Update.vue
Normal file
113
resources/assets/src/components/admin/Update.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<span>
|
||||
<button
|
||||
v-if="!updating"
|
||||
class="btn btn-primary"
|
||||
:disabled="!canUpdate"
|
||||
@click="update"
|
||||
>{{ $t('admin.updateButton') }}</button>
|
||||
<button v-else disabled class="btn btn-primary">
|
||||
<i class="fa fa-spinner fa-spin"></i> {{ $t('admin.preparing') }}
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="modal-start-download"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" v-t="'admin.downloading'"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ $t('admin.updateSize') }}<span>{{ total }}</span> KB</p>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar progress-bar-striped active"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
:style="{ width: `${percentage}%` }"
|
||||
>
|
||||
<span>{{ ~~percentage }}</span>%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { swal } from '../../js/notify';
|
||||
|
||||
const POLLING_INTERVAL = 500;
|
||||
|
||||
export default {
|
||||
name: 'UpdateButton',
|
||||
data: () => ({
|
||||
canUpdate: blessing.extra.canUpdate,
|
||||
updating: false,
|
||||
total: 0,
|
||||
downloaded: 0,
|
||||
}),
|
||||
computed: {
|
||||
percentage() {
|
||||
return this.downloaded / this.total * 100;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async update() {
|
||||
this.updating = true;
|
||||
|
||||
await this.takeAction('prepare-download');
|
||||
|
||||
this.updating && $('#modal-start-download').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
setTimeout(this.polling, POLLING_INTERVAL);
|
||||
|
||||
this.updating && await this.takeAction('start-download');
|
||||
|
||||
this.updating && await this.takeAction('extract');
|
||||
|
||||
this.updating = false;
|
||||
if (this.downloaded) {
|
||||
await swal({ type: 'success', text: this.$t('admin.updateCompleted') });
|
||||
window.location = blessing.base_url;
|
||||
}
|
||||
},
|
||||
async takeAction(action) {
|
||||
const { errno, msg } = await this.$http.post('/admin/update/download', {
|
||||
action
|
||||
});
|
||||
if (errno && errno !== 0) {
|
||||
swal({ type: 'error', text: msg });
|
||||
this.updating = false;
|
||||
}
|
||||
},
|
||||
async polling() {
|
||||
const { downloaded, total } = await this.$http.get(
|
||||
'/admin/update/download',
|
||||
{ action: 'get-progress' }
|
||||
);
|
||||
this.downloaded = ~~(+downloaded / 1024);
|
||||
this.total = ~~(+total / 1024);
|
||||
|
||||
this.updating && setTimeout(this.polling, POLLING_INTERVAL);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -44,6 +44,11 @@ export default [
|
||||
component: () => import('./admin/Market'),
|
||||
el: '.content'
|
||||
},
|
||||
{
|
||||
path: 'admin/update',
|
||||
component: () => import('./admin/Update'),
|
||||
el: '#update-button'
|
||||
},
|
||||
{
|
||||
path: 'auth/login',
|
||||
component: () => import('./auth/Login'),
|
||||
|
@ -1,110 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
async function downloadUpdates() {
|
||||
console.log('Prepare trno download');
|
||||
|
||||
try {
|
||||
const preparation = await fetch({
|
||||
url: url('admin/update/download?action=prepare-download'),
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
$('#update-button').html(
|
||||
'<i class="fa fa-spinner fa-spin"></i> ' + trans('admin.preparing')
|
||||
).prop('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
console.log(preparation);
|
||||
|
||||
const { file_size: fileSize } = preparation;
|
||||
|
||||
$('#file-size').html(fileSize);
|
||||
|
||||
$('#modal-start-download').modal({
|
||||
'backdrop': 'static',
|
||||
'keyboard': false
|
||||
});
|
||||
|
||||
console.log('Start downloading');
|
||||
|
||||
// Downloading progress polling
|
||||
const interval_id = setInterval(progressPolling(fileSize), 300);
|
||||
|
||||
const download = await fetch({
|
||||
url: url('admin/update/download?action=start-download'),
|
||||
type: 'POST',
|
||||
dataType: 'json'
|
||||
});
|
||||
|
||||
clearInterval(interval_id);
|
||||
|
||||
console.log('Downloading finished');
|
||||
console.log(download);
|
||||
|
||||
$('.modal-title').html('<i class="fa fa-spinner fa-spin"></i> ' + trans('admin.extracting'));
|
||||
$('.modal-body').append(`<p>${trans('admin.downloadCompleted')}</p>`);
|
||||
|
||||
console.log('Start extracting');
|
||||
|
||||
const extract = await fetch({
|
||||
url: url('admin/update/download?action=extract'),
|
||||
type: 'POST',
|
||||
dataType: 'json'
|
||||
});
|
||||
|
||||
console.log('Package extracted and files are covered');
|
||||
$('#modal-start-download').modal('toggle');
|
||||
|
||||
swal({
|
||||
type: 'success',
|
||||
html: extract.msg
|
||||
}).then(function () {
|
||||
window.location = url('/');
|
||||
}, function () {
|
||||
window.location = url('/');
|
||||
});
|
||||
} catch (error) {
|
||||
showAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function progressPolling(fileSize) {
|
||||
return async () => {
|
||||
try {
|
||||
const { size } = await fetch({
|
||||
url: url('admin/update/download?action=get-file-size'),
|
||||
type: 'GET'
|
||||
});
|
||||
|
||||
const progress = (size / fileSize * 100).toFixed(2);
|
||||
|
||||
$('#imported-progress').html(progress);
|
||||
$('.progress-bar')
|
||||
.css('width', progress + '%')
|
||||
.attr('aria-valuenow', progress);
|
||||
} catch (error) {
|
||||
// No need to show error if failed to get size
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
const data = await fetch({ url: url('admin/update/check') });
|
||||
if (data.available === true) {
|
||||
const dom = `<span class="label label-primary pull-right">v${data.latest}</span>`;
|
||||
|
||||
$(`[href="${url('admin/update')}"]`).append(dom);
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
module.exports = {
|
||||
checkForUpdates,
|
||||
progressPolling,
|
||||
downloadUpdates,
|
||||
};
|
||||
}
|
55
resources/assets/tests/components/admin/Update.test.js
Normal file
55
resources/assets/tests/components/admin/Update.test.js
Normal file
@ -0,0 +1,55 @@
|
||||
import Vue from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { flushPromises } from '../../utils';
|
||||
import Update from '@/components/admin/Update.vue';
|
||||
import '@/js/notify';
|
||||
|
||||
jest.mock('@/js/notify');
|
||||
|
||||
afterEach(() => {
|
||||
window.blessing.extra = { canUpdate: true };
|
||||
});
|
||||
|
||||
test('button should be disabled if update is unavailable', () => {
|
||||
window.blessing.extra = { canUpdate: false };
|
||||
const wrapper = mount(Update);
|
||||
expect(wrapper.find('.btn').attributes('disabled')).toBe('disabled');
|
||||
});
|
||||
|
||||
test('perform update', async () => {
|
||||
window.$ = jest.fn(() => ({
|
||||
modal() {}
|
||||
}));
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1 })
|
||||
.mockResolvedValue({});
|
||||
Vue.prototype.$http.get
|
||||
.mockResolvedValue({ total: 2048, downloaded: 2048 });
|
||||
const wrapper = mount(Update);
|
||||
const button = wrapper.find('.btn');
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(window.$).not.toBeCalled();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/admin/update/download',
|
||||
{ action: 'prepare-download' }
|
||||
);
|
||||
|
||||
button.trigger('click');
|
||||
jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(window.$).toBeCalled();
|
||||
expect(Vue.prototype.$http.get).toBeCalledWith(
|
||||
'/admin/update/download',
|
||||
{ action: 'get-progress' }
|
||||
);
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/admin/update/download',
|
||||
{ action: 'start-download' }
|
||||
);
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/admin/update/download',
|
||||
{ action: 'extract' }
|
||||
);
|
||||
});
|
@ -291,9 +291,11 @@ admin:
|
||||
may be not compatible with the current version of Blessing Skin, and
|
||||
enabling it may cause unexpected problems. Do you really want to enable the
|
||||
plugin?
|
||||
updateButton: Update Now
|
||||
updateSize: "Size of package:"
|
||||
preparing: Preparing
|
||||
downloadCompleted: Update package download completed.
|
||||
extracting: Extracting update package..
|
||||
downloading: Downloading update package...
|
||||
updateCompleted: Update completed.
|
||||
change-color:
|
||||
title: Change theme color
|
||||
success: Theme color updated.
|
||||
|
@ -285,9 +285,11 @@ admin:
|
||||
noDependenciesNotice: >-
|
||||
此插件没有声明任何依赖关系,这代表它有可能并不兼容此版本的 Blessing
|
||||
Skin,请将此插件升级至可能的最新版本。强行启用可能导致无法预料的后果。你确定要启用此插件吗?
|
||||
updateButton: 马上升级
|
||||
updateSize: 更新包大小:
|
||||
preparing: 正在准备
|
||||
downloadCompleted: 更新包下载完成
|
||||
extracting: 正在解压更新包
|
||||
downloading: 正在下载更新包
|
||||
updateCompleted: 更新完成
|
||||
change-color:
|
||||
title: 更改配色
|
||||
success: 修改配色成功
|
||||
|
@ -94,7 +94,7 @@
|
||||
@endif
|
||||
</div><!-- /.box-body -->
|
||||
<div class="box-footer">
|
||||
<a class="btn btn-primary" id="update-button" {!! !$info['new_version_available'] ? 'disabled="disabled"' : 'onclick="downloadUpdates();"' !!}>@lang('admin.update.info.button')</a>
|
||||
<span id="update-button"></span>
|
||||
{!! trans('admin.update.info.check-github', ['url' => 'https://github.com/printempw/blessing-skin-server/releases']) !!}
|
||||
</div>
|
||||
</div>
|
||||
@ -132,4 +132,10 @@
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<script>
|
||||
blessing.extra = {
|
||||
canUpdate: {{ $info['new_version_available'] ? 'true' : 'false' }}
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
Loading…
Reference in New Issue
Block a user