Add initial repair dialog
This commit is contained in:
187
dashboard/src/components/AppRepairDialog.vue
Normal file
187
dashboard/src/components/AppRepairDialog.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<script setup>
|
||||
|
||||
import { ref, useTemplateRef } from 'vue';
|
||||
import { Dialog, FormGroup, SingleSelect, TextInput } from 'pankow';
|
||||
import { prettyDate } from 'pankow/utils';
|
||||
import { ISTATES } from '../constants.js';
|
||||
import { taskNameFromInstallationState } from '../utils.js';
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
|
||||
const appsModel = AppsModel.create();
|
||||
|
||||
const emit = defineEmits([ 'success' ]);
|
||||
|
||||
const dialog = useTemplateRef('dialog');
|
||||
|
||||
const busy = ref(false);
|
||||
const formError = ref('');
|
||||
const id = ref('');
|
||||
const fqdn = ref('');
|
||||
const appError = ref(null);
|
||||
const backups = ref([]);
|
||||
const backupId = ref('');
|
||||
|
||||
async function onSubmit() {
|
||||
busy.value = true;
|
||||
formError.value = '';
|
||||
|
||||
const errorState = (appError.value && appError.value.installationState) || ISTATES.PENDING_CONFIGURE;
|
||||
const data = {};
|
||||
|
||||
let repairFunc;
|
||||
switch (errorState) {
|
||||
case ISTATES.PENDING_INSTALL:
|
||||
case ISTATES.PENDING_CLONE: // if manifest or bad image, use CLI to provide new manifest
|
||||
repairFunc = appsModel.repair.bind(null, id.value, {}); // this will trigger a re-install
|
||||
break;
|
||||
|
||||
// case ISTATES.PENDING_LOCATION_CHANGE:
|
||||
// data.subdomain = $scope.repair.subdomain;
|
||||
// data.domain = $scope.repair.domain.domain;
|
||||
// data.aliasDomains = $scope.repair.aliasDomains.filter(function (a) { return a.enabled; })
|
||||
// .map(function (d) { return { subdomain: d.subdomain, domain: d.domain.domain }; });
|
||||
// data.redirectDomains = $scope.repair.redirectDomains.filter(function (a) { return a.enabled; })
|
||||
// .map(function (d) { return { subdomain: d.subdomain, domain: d.domain.domain }; });
|
||||
// data.overwriteDns = true; // always overwriteDns. user can anyway check and uncheck above
|
||||
// repairFunc = Client.configureApp.bind(null, $scope.app.id, 'location', data);
|
||||
// break;
|
||||
|
||||
case ISTATES.PENDING_DATA_DIR_MIGRATION:
|
||||
repairFunc = appsModel.configure.bind(null, id.value, 'storage', { storageVolumeId: null, storageVolumePrefix: null });
|
||||
break;
|
||||
|
||||
// this also happens for import faliures. this UI can only show backup listing. use CLI for arbit id/config
|
||||
case ISTATES.PENDING_RESTORE:
|
||||
case ISTATES.PENDING_IMPORT:
|
||||
if (backups.value.length === 0) { // this can happen when you give some invalid backup via CLI and restore via UI
|
||||
repairFunc = appsModel.repair.bind(null, id.value, {}); // this will trigger a re-install
|
||||
} else {
|
||||
repairFunc = appsModel.restore.bind(null, id.value, backupId.value);
|
||||
}
|
||||
break;
|
||||
|
||||
case ISTATES.PENDING_UNINSTALL:
|
||||
repairFunc = appsModel.uninstall.bind(null, id.value);
|
||||
break;
|
||||
|
||||
case ISTATES.PENDING_START:
|
||||
case ISTATES.PENDING_STOP:
|
||||
case ISTATES.PENDING_RESTART:
|
||||
case ISTATES.PENDING_RESIZE:
|
||||
case ISTATES.PENDING_DEBUG:
|
||||
case ISTATES.PENDING_RECREATE_CONTAINER:
|
||||
case ISTATES.PENDING_CONFIGURE:
|
||||
case ISTATES.PENDING_BACKUP: // can happen if the backup task was killed/rebooted
|
||||
case ISTATES.PENDING_UPDATE: // when update failed, just bring it back to current state and user can click update again
|
||||
default:
|
||||
repairFunc = appsModel.repair.bind(null, id.value, {});
|
||||
break;
|
||||
}
|
||||
|
||||
const [error] = await repairFunc();
|
||||
if (error) {
|
||||
busy.value = false;
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
emit('success');
|
||||
dialog.value.close();
|
||||
busy.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
async open(app) {
|
||||
id.value = app.id;
|
||||
fqdn.value = app.fqdn;
|
||||
appError.value = app.error || null;
|
||||
|
||||
const errorState = (app.error && app.error.installationState) || ISTATES.PENDING_CONFIGURE;
|
||||
|
||||
if (errorState === ISTATES.PENDING_RESTORE || errorState === ISTATES.PENDING_IMPORT) {
|
||||
const [error, result] = await appsModel.backups(app.id);
|
||||
if (error) return console.error(error);
|
||||
|
||||
result.forEach(b => {
|
||||
b.label = prettyDate(b.creationTime) + ` - v${b.packageVersion}`;
|
||||
});
|
||||
backups.value = result;
|
||||
backupId.value = '';
|
||||
}
|
||||
|
||||
dialog.value.open();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog ref="dialog" :title="$t('app.repairDialog.title', { app: fqdn })"
|
||||
:confirm-label="$t('app.repairDialog.retryAction', { task: taskNameFromInstallationState(appError ? appError.installationState : '') })"
|
||||
:confirm-active="!busy"
|
||||
:confirm-busy="busy"
|
||||
:reject-label="busy ? '' : $t('main.dialog.cancel')"
|
||||
reject-style="secondary"
|
||||
@confirm="onSubmit()"
|
||||
>
|
||||
<div>
|
||||
<div class="text-danger" v-if="formError">{{ formError }}</div>
|
||||
|
||||
<div v-if="appError">
|
||||
<p v-html="$t('app.repairDialog.taskError', { task: taskNameFromInstallationState(appError ? appError.installationState : '') })"></p>
|
||||
<p class="text-danger">{{ appError.reason + ': ' + appError.message }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>{{ $t('app.repairDialog.description') }}</p>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="onSubmit()" autocomplete="off">
|
||||
<fieldset :disabled="busy">
|
||||
<FormGroup v-if="backups.length">
|
||||
<label for="backupInput">{{ $t('app.repairDialog.fromBackup') }}</label>
|
||||
<SingleSelect :options="backups" v-model="backupId" option-key="id" option-label="label"/>
|
||||
</FormGroup>
|
||||
<!--
|
||||
<div class="form-group" ng-show="repair.subdomain && repair.domain">
|
||||
<p>{{ 'app.repairDialog.domainDescription' | tr }}</p>
|
||||
<label class="control-label">{{ 'app.repairDialog.location' | tr }}</label>
|
||||
<div class="input-group form-inline">
|
||||
<input type="text" class="form-control" ng-model="repair.subdomain" name="location" placeholder="{{ 'Leave empty to use bare domain' }}" autofocus>
|
||||
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<span>{{ '.' + repair.domain.domain }}</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
<li ng-repeat="domain in domains">
|
||||
<a href="" ng-click="repair.domain = domain">{{ domain.domain }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="repair.aliasDomains.length">
|
||||
<p ng-repeat="aliasDomain in repair.aliasDomains">
|
||||
<label class="control-label"><input type="checkbox" ng-model="aliasDomain.enabled">
|
||||
{{ aliasDomain.subdomain + (!aliasDomain.subdomain ? '' : '.') + aliasDomain.domain.domain }}
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="repair.redirectDomains.length">
|
||||
<p ng-repeat="redirectDomain in repair.redirectDomains">
|
||||
<label class="control-label"><input type="checkbox" ng-model="redirectDomain.enabled">
|
||||
{{ redirectDomain.subdomain + (!redirectDomain.subdomain ? '' : '.') + redirectDomain.domain.domain }}
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
-->
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user