144 lines
5.1 KiB
Vue
144 lines
5.1 KiB
Vue
<script setup>
|
|
|
|
import { ref, watch, onMounted, useTemplateRef } from 'vue';
|
|
import { marked } from 'marked';
|
|
import { Button, Switch, Dialog, Checkbox } from 'pankow';
|
|
import AppsModel from '../../models/AppsModel.js';
|
|
import UpdaterModel from '../../models/UpdaterModel.js';
|
|
import ProfileModel from '../../models/ProfileModel.js';
|
|
|
|
const props = defineProps([ 'app' ]);
|
|
|
|
const appsModel = AppsModel.create();
|
|
const updaterModel = UpdaterModel.create();
|
|
const profileModel = ProfileModel.create();
|
|
|
|
const dialog = useTemplateRef('dialog');
|
|
const update = ref(null);
|
|
const profile = ref({});
|
|
const busyUpdate = ref(false);
|
|
const busyCheck = ref(false);
|
|
const skipBackup = ref(false);
|
|
const updateError = ref('');
|
|
const autoUpdatesEnabled = ref(false);
|
|
const autoUpdatesEnabledBusy = ref(false);
|
|
|
|
async function onAutoUpdatesEnabledChange(value) {
|
|
autoUpdatesEnabledBusy.value = true;
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'automatic_update', { enable: value });
|
|
if (error) {
|
|
autoUpdatesEnabled.value = !value;
|
|
console.error(error);
|
|
}
|
|
|
|
autoUpdatesEnabledBusy.value = false;
|
|
}
|
|
|
|
async function refreshUpdates() {
|
|
const [error, result] = await updaterModel.info();
|
|
if (error) return console.error(error);
|
|
|
|
const appUpdate = result[props.app.id] || null;
|
|
if (!appUpdate) update.value = null;
|
|
else if (!appUpdate.manifest) update.value = null;
|
|
else if (!appUpdate.manifest.version) update.value = null;
|
|
else if (appUpdate.manifest.version === props.app.manifest.version) update.value = null;
|
|
else update.value = appUpdate;
|
|
}
|
|
|
|
async function onCheck() {
|
|
busyCheck.value = true;
|
|
|
|
const [error] = await appsModel.checkForUpdates(props.app.id);
|
|
if (error) return console.error(error);
|
|
|
|
await refreshUpdates();
|
|
|
|
busyCheck.value = false;
|
|
}
|
|
|
|
async function onUpdate() {
|
|
busyUpdate.value = true;
|
|
updateError.value = '';
|
|
|
|
let [error] = await appsModel.update(props.app.id, update.value.manifest, skipBackup.value);
|
|
if (error) {
|
|
busyUpdate.value = false;
|
|
if (error.status === 400) updateError.value = error.body ? error.body.message : 'Internal error';
|
|
return console.error(error);
|
|
}
|
|
|
|
update.value = null;
|
|
|
|
dialog.value.close();
|
|
|
|
[error] = await appsModel.checkForUpdates(props.app.id);
|
|
if (error) return console.error(error);
|
|
}
|
|
|
|
function onAskUpdate() {
|
|
busyUpdate.value = false;
|
|
dialog.value.open();
|
|
}
|
|
|
|
function onSetupSubscription() {
|
|
// TODO payment
|
|
}
|
|
|
|
onMounted(async () => {
|
|
busyUpdate.value = false;
|
|
busyCheck.value = false;
|
|
autoUpdatesEnabled.value = props.app.enableAutomaticUpdate;
|
|
await refreshUpdates();
|
|
|
|
const [error, result] = await profileModel.get();
|
|
if (error) return console.error(error);
|
|
|
|
profile.value = result;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<Dialog v-if="update" ref="dialog"
|
|
:title="$t('app.updateDialog.title', { app: app.fqdn })"
|
|
:reject-label="$t('main.dialog.cancel')"
|
|
reject-style="secondary"
|
|
:confirm-label="$t('app.updateDialog.updateAction')"
|
|
:confirm-active="!busyUpdate && update.manifest.dockerImage"
|
|
:confirm-busy="busyUpdate"
|
|
:alternate-label="!update.manifest.dockerImage && profile.isAtLeastOwner ? $t('app.updateDialog.setupSubscriptionAction') : ''"
|
|
alternate-style="success"
|
|
@confirm="onUpdate()"
|
|
@alternate="onSetupSubscription()"
|
|
>
|
|
<div>
|
|
<div class="error-label" v-if="!update.manifest.dockerImage">{{ $t('app.updateDialog.subscriptionExpired') }}</div>
|
|
<div class="error-label" v-if="updateError">{{ updateError }}</div>
|
|
|
|
<p class="text-danger" v-if="update.unstable">{{ $t('app.updateDialog.unstableWarning') }}</p>
|
|
<p>{{ $t('app.updateDialog.changelogHeader', { version: update.manifest.version }) }}</p>
|
|
<div v-html="marked.parse(update.manifest.changelog)"></div>
|
|
|
|
<Checkbox v-if="update.manifest.dockerImage" v-model="skipBackup" :label="$t('app.updateDialog.skipBackupCheckbox')" />
|
|
</div>
|
|
</Dialog>
|
|
|
|
<label>{{ $t('app.updatesTabTitle') }}</label>
|
|
|
|
<p v-html="$t('app.updates.auto.description', { appStoreLink: 'https://www.cloudron.io/store/index.html' })"></p>
|
|
<p v-if="!app.appStoreId" class="text-danger">{{ $t('app.updates.info.customAppUpdateInfo') }}</p>
|
|
|
|
<Switch v-if="app.appStoreId" v-model="autoUpdatesEnabled" :disabled="autoUpdatesEnabledBusy" :label="$t(autoUpdatesEnabled ? 'app.updates.auto.enabled' : 'app.updates.auto.disabled')" @change="onAutoUpdatesEnabledChange"/>
|
|
|
|
<div style="display: flex; gap: 6px; margin-top: 20px">
|
|
<Button v-if="app.appStoreId" @click="onCheck()" :disabled="busyCheck" :loading="busyCheck">{{ $t('settings.updates.checkForUpdatesAction') }}</Button>
|
|
|
|
<!-- show update button only if update available -->
|
|
<Button v-if="update" :danger="update.unstable ? true : null" :success="update.unstable ? null : true" @click="onAskUpdate()" :disabled="app.taskId || app.error || app.runState === 'stopped' || app.installationState === 'pending_update'" v-tooltip="(app.error || app.taskId || app.runState === 'stopped') ? (app.error ? 'App is in error state' : 'App is not running') : ''">{{ $t('app.updateDialog.updateAction') }}</Button>
|
|
</div>
|
|
</div>
|
|
</template>
|