Implement update dialog
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
<script setup>
|
||||
|
||||
import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { Button, Dialog, ProgressBar, Radiobutton, MultiSelect } from 'pankow';
|
||||
import { ref, onMounted, useTemplateRef, computed } from 'vue';
|
||||
import { marked } from 'marked';
|
||||
import { Button, Dialog, ProgressBar, Radiobutton, MultiSelect, Checkbox } from 'pankow';
|
||||
import { prettyLongDate } from 'pankow/utils';
|
||||
import { TASK_TYPES } from '../constants.js';
|
||||
import { TASK_TYPES, ISTATES } from '../constants.js';
|
||||
import Section from '../components/Section.vue';
|
||||
import SettingsItem from '../components/SettingsItem.vue';
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
import UpdaterModel from '../models/UpdaterModel.js';
|
||||
import TasksModel from '../models/TasksModel.js';
|
||||
import DashboardModel from '../models/DashboardModel.js';
|
||||
|
||||
const appsModel = AppsModel.create();
|
||||
const tasksModel = TasksModel.create();
|
||||
const updaterModel = UpdaterModel.create();
|
||||
const dashboardModel = DashboardModel.create();
|
||||
@@ -47,15 +50,32 @@ function prettyAutoUpdateSchedule(pattern) {
|
||||
}
|
||||
}
|
||||
|
||||
const updateDialog = useTemplateRef('updateDialog');
|
||||
|
||||
const taskLogsMenu = ref([]);
|
||||
const apps = ref([]);
|
||||
const version = ref('');
|
||||
const ubuntuVersion = ref('');
|
||||
const currentPattern = ref('');
|
||||
const percent = ref(0);
|
||||
const message = ref('');
|
||||
const updateBusy = ref(false);
|
||||
const updateError = ref({});
|
||||
const checkingBusy = ref(false);
|
||||
const pendingUpdate = ref(null);
|
||||
const skipBackup = ref(false);
|
||||
|
||||
const canUpdate = computed(() => {
|
||||
return apps.value.every(function (app) {
|
||||
return (app.installationState === ISTATES.ERROR) || (app.installationState === ISTATES.INSTALLED);
|
||||
});
|
||||
});
|
||||
|
||||
const inProgressApps = computed(() => {
|
||||
return apps.value.filter(function (app) {
|
||||
return app.installationState !== ISTATES.ERROR && app.installationState !== ISTATES.INSTALLED;
|
||||
});
|
||||
});
|
||||
|
||||
const configureDialog = useTemplateRef('configureDialog');
|
||||
const configureBusy = ref(false);
|
||||
@@ -76,6 +96,13 @@ async function refreshAutoupdatePattern() {
|
||||
configurePattern.value = result.pattern;
|
||||
}
|
||||
|
||||
async function refreshApps() {
|
||||
const [error, result] = await appsModel.list();
|
||||
if (error) return console.error(error);
|
||||
|
||||
apps.value = result;
|
||||
}
|
||||
|
||||
async function refreshInfo() {
|
||||
const [error, result] = await updaterModel.getBoxUpdate();
|
||||
if (error) return console.error(error);
|
||||
@@ -128,8 +155,66 @@ async function onSubmitConfigure() {
|
||||
configureDialog.value.close();
|
||||
}
|
||||
|
||||
function onShowUpdate() {
|
||||
async function onShowUpdate() {
|
||||
skipBackup.value = false;
|
||||
await refreshApps();
|
||||
updateDialog.value.open();
|
||||
}
|
||||
|
||||
const lastTask = ref({});
|
||||
async function waitForTask() {
|
||||
if (!lastTask.value.id) return;
|
||||
|
||||
const [error, result] = await tasksModel.get(lastTask.value.id);
|
||||
if (error) return console.error(error);
|
||||
|
||||
lastTask.value = result;
|
||||
|
||||
// task done, refresh menu
|
||||
if (!result.active) {
|
||||
refreshInfo();
|
||||
refreshTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(waitForTask, 2000);
|
||||
}
|
||||
|
||||
async function refreshTasks() {
|
||||
const [error, result] = await tasksModel.getByType(TASK_TYPES.TASK_UPDATE);
|
||||
if (error) return console.error(error);
|
||||
|
||||
lastTask.value = result[0] || {};
|
||||
|
||||
taskLogsMenu.value = result.map(t => {
|
||||
return {
|
||||
icon: 'fa-solid ' + ((!t.active && t.success) ? 'status-active fa-check-circle' : (t.active ? 'fa-circle-notch fa-spin' : 'status-error fa-times-circle')),
|
||||
label: prettyLongDate(t.ts),
|
||||
action: () => { window.open(`/logs.html?taskId=${t.id}`); }
|
||||
};
|
||||
});
|
||||
|
||||
// if last task is currently active, start polling
|
||||
if (lastTask.value.active) waitForTask();
|
||||
}
|
||||
|
||||
async function onSubmitUpdate() {
|
||||
updateError.value = {};
|
||||
updateBusy.value = true;
|
||||
percent.value = 0;
|
||||
message.value = '';
|
||||
|
||||
const [error] = await updaterModel.update(skipBackup.value);
|
||||
updateBusy.value = false;
|
||||
|
||||
if (error) {
|
||||
updateError.value.generic = error.message || 'Internal error';
|
||||
return;
|
||||
}
|
||||
|
||||
await refreshTasks();
|
||||
|
||||
updateDialog.value.close();
|
||||
}
|
||||
|
||||
async function onCheck() {
|
||||
@@ -142,19 +227,6 @@ async function onCheck() {
|
||||
checkingBusy.value = false;
|
||||
}
|
||||
|
||||
async function refreshTasks() {
|
||||
const [error, result] = await tasksModel.getByType(TASK_TYPES.TASK_UPDATE);
|
||||
if (error) return console.error(error);
|
||||
|
||||
taskLogsMenu.value = result.map(t => {
|
||||
return {
|
||||
icon: 'fa-solid ' + ((!t.active && t.success) ? 'status-active fa-check-circle' : (t.active ? 'fa-circle-notch fa-spin' : 'status-error fa-times-circle')),
|
||||
label: prettyLongDate(t.ts),
|
||||
action: () => { window.open(`/logs.html?taskId=${t.id}`); }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const [error, result] = await dashboardModel.config();
|
||||
if (error) return console.error(error);
|
||||
@@ -171,6 +243,42 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog ref="updateDialog"
|
||||
:title="$t('settings.updateDialog.title') + ` v${pendingUpdate ? pendingUpdate.version : ''}`"
|
||||
:confirm-label="$t('settings.updateDialog.updateAction')"
|
||||
:confirm-active="canUpdate"
|
||||
:confirm-busy="updateBusy"
|
||||
:confirm-style="pendingUpdate && pendingUpdate.unstable ? 'danger' : 'primary'"
|
||||
:reject-label="updateBusy ? null : $t('main.dialog.cancel')"
|
||||
reject-style="secondary"
|
||||
@confirm="onSubmitUpdate()"
|
||||
>
|
||||
<div v-if="pendingUpdate">
|
||||
<div v-if="canUpdate">
|
||||
<p class="text-danger" v-if="pendingUpdate.unstable">{{ $t('settings.updateDialog.unstableWarning') }}</p>
|
||||
<p>{{ $t('settings.updateDialog.changes') }}:</p>
|
||||
<ul>
|
||||
<li v-for="change in pendingUpdate.changelog" :key="change">{{ change }}</li>
|
||||
<li v-for="change in pendingUpdate.changelog" :key="change" v-html="marked.parse(change)"></li>
|
||||
</ul>
|
||||
<br/>
|
||||
<p v-if="updateError.generic" class="error-label">{{ updateError.generic }}</p>
|
||||
|
||||
<Checkbox v-model="skipBackup" :label="$t('settings.updateDialog.skipBackupCheckbox')"/>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<p>{{ $t('settings.updateDialog.blockingApps') }}</p>
|
||||
<ul>
|
||||
<li v-for="app in inProgressApps" :key="app.id">{{ app.fqdn }}</li>
|
||||
</ul>
|
||||
<span>{{ $t('settings.updateDialog.blockingAppsInfo') }}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<Dialog ref="configureDialog"
|
||||
:title="$t('settings.updateScheduleDialog.title')"
|
||||
:confirm-label="$t('main.dialog.save')"
|
||||
|
||||
Reference in New Issue
Block a user