Move app restore dialog to its own component for reuse
This commit is contained in:
@@ -4,14 +4,13 @@ import { useI18n } from 'vue-i18n';
|
||||
const i18n = useI18n();
|
||||
const t = i18n.t;
|
||||
|
||||
import { ref, onMounted, useTemplateRef, computed } from 'vue';
|
||||
import { Button, ButtonGroup, InputGroup, FormGroup, TextInput, SingleSelect, TableView, InputDialog, Dialog } from 'pankow';
|
||||
import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { Button, ButtonGroup, TableView, InputDialog } from 'pankow';
|
||||
import { prettyLongDate } from 'pankow/utils';
|
||||
import { API_ORIGIN, SECRET_PLACEHOLDER } from '../constants.js';
|
||||
import AppRestoreDialog from '../components/AppRestoreDialog.vue';
|
||||
import Section from '../components/Section.vue';
|
||||
import PortBindings from '../components/PortBindings.vue';
|
||||
import ArchivesModel from '../models/ArchivesModel.js';
|
||||
import DomainsModel from '../models/DomainsModel.js';
|
||||
import { download } from '../utils.js';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -19,7 +18,6 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const archivesModel = ArchivesModel.create();
|
||||
const domainsModel = DomainsModel.create();
|
||||
|
||||
const columns = {
|
||||
icon: {}, // archived
|
||||
@@ -75,152 +73,9 @@ async function onRemove(archive) {
|
||||
}
|
||||
|
||||
const restoreDialog = useTemplateRef('restoreDialog');
|
||||
const restoreArchive = ref({});
|
||||
const restoreFqdn = ref('');
|
||||
const restoreManifest = ref({});
|
||||
const restoreLocation = ref('');
|
||||
const restoreDomain = ref('');
|
||||
const restoreNeedsOverwrite = ref(false);
|
||||
const restoreBusy = ref(false);
|
||||
const restoreError = ref({});
|
||||
const restoreTcpPorts = ref({});
|
||||
const restoreUdpPorts = ref({});
|
||||
const domains = ref([]);
|
||||
const restoreSecondaryDomains = ref([]);
|
||||
const restoreDomainProvider = computed(() => {
|
||||
const tmp = domains.value.find((d) => d.domain === restoreDomain.value);
|
||||
return tmp ? tmp.provider : '';
|
||||
});
|
||||
|
||||
async function onRestore(archive) {
|
||||
restoreBusy.value = false;
|
||||
restoreError.value = {};
|
||||
|
||||
const [error, result] = await domainsModel.list();
|
||||
if (error) return console.error(error);
|
||||
|
||||
domains.value = result;
|
||||
|
||||
const app = archive.appConfig || {
|
||||
subdomain: '',
|
||||
domain: domains.value[0].domain,
|
||||
secondaryDomains: [],
|
||||
portBindings: {}
|
||||
}; // pre-8.2 backups do not have appConfig
|
||||
|
||||
// appConfig also has a manifest but it has incomplete port objects!
|
||||
const manifest = archive.manifest;
|
||||
|
||||
restoreLocation.value = app.subdomain;
|
||||
const d = domains.value.find(function (d) { return app.domain === d.domain; });
|
||||
restoreDomain.value = d ? d.domain : domains.value[0].domain; // try to pre-select the app's domain
|
||||
restoreSecondaryDomains.value = {};
|
||||
restoreNeedsOverwrite.value = false;
|
||||
restoreArchive.value = archive;
|
||||
restoreFqdn.value = archive.appConfig?.fqdn || '-';
|
||||
restoreManifest.value = manifest;
|
||||
|
||||
const httpPorts = manifest.httpPorts || {};
|
||||
for (const env in httpPorts) {
|
||||
restoreSecondaryDomains.value[env] = {
|
||||
title: httpPorts[env].title,
|
||||
description: httpPorts[env].description,
|
||||
subdomain: httpPorts[env].defaultValue || '',
|
||||
domain: restoreDomain.value,
|
||||
};
|
||||
}
|
||||
|
||||
// now fill secondaryDomains with real values, if exists
|
||||
if (app.secondaryDomains) {
|
||||
app.secondaryDomains.forEach(function (sd) {
|
||||
const usedDomain = domains.value.find(function (d) { return sd.domain === d.domain; });
|
||||
|
||||
restoreSecondaryDomains.value[sd.environmentVariable].subdomain = sd.subdomain;
|
||||
restoreSecondaryDomains.value[sd.environmentVariable].domain = usedDomain ? usedDomain.domain : restoreDomain.value;
|
||||
});
|
||||
}
|
||||
|
||||
// Portbinding map only for information we make copies
|
||||
restoreTcpPorts.value = manifest.tcpPorts ? JSON.parse(JSON.stringify(manifest.tcpPorts)) : {};
|
||||
restoreUdpPorts.value = manifest.udpPorts ? JSON.parse(JSON.stringify(manifest.udpPorts)) : {};
|
||||
|
||||
// set default ports for tcp
|
||||
for (const env in restoreTcpPorts.value) {
|
||||
if (app.portBindings[env]) { // was enabled in the app
|
||||
restoreTcpPorts.value[env].value = app.portBindings[env].hostPort;
|
||||
restoreTcpPorts.value[env].enabled = true;
|
||||
} else {
|
||||
restoreTcpPorts.value[env].value = restoreTcpPorts.value[env].defaultValue || 0;
|
||||
restoreTcpPorts.value[env].enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// set default ports for udp
|
||||
for (const env in restoreUdpPorts.value) {
|
||||
if (app.portBindings[env]) { // was enabled in the app
|
||||
restoreUdpPorts.value[env].value = app.portBindings[env].hostPort;
|
||||
restoreUdpPorts.value[env].enabled = true;
|
||||
} else {
|
||||
restoreUdpPorts.value[env].value = restoreUdpPorts.value[env].defaultValue || 0;
|
||||
restoreUdpPorts.value[env].enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
restoreDialog.value.open();
|
||||
}
|
||||
|
||||
async function onRestoreSubmit() {
|
||||
restoreBusy.value = true;
|
||||
restoreError.value = {};
|
||||
|
||||
const secondaryDomains = {};
|
||||
for (const env in restoreSecondaryDomains.value) {
|
||||
secondaryDomains[env] = {
|
||||
subdomain: restoreSecondaryDomains.value[env].subdomain,
|
||||
domain: restoreSecondaryDomains.value[env].domain
|
||||
};
|
||||
}
|
||||
|
||||
// only use enabled ports
|
||||
const finalPorts = {};
|
||||
for (const env in restoreTcpPorts.value) {
|
||||
if (restoreTcpPorts.value[env].enabled) {
|
||||
finalPorts[env] = restoreTcpPorts.value[env].value;
|
||||
}
|
||||
}
|
||||
for (const env in restoreUdpPorts.value) {
|
||||
if (restoreUdpPorts.value[env].enabled) {
|
||||
finalPorts[env] = restoreUdpPorts.value[env].value;
|
||||
}
|
||||
}
|
||||
|
||||
const data = {
|
||||
subdomain: restoreLocation.value,
|
||||
domain: restoreDomain.value,
|
||||
secondaryDomains,
|
||||
ports: finalPorts,
|
||||
overwriteDns: restoreNeedsOverwrite.value,
|
||||
};
|
||||
|
||||
const [error] = await archivesModel.restore(restoreArchive.value.id, data);
|
||||
if (error) {
|
||||
if (error.type === 'externally_exists') {
|
||||
restoreError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.';
|
||||
restoreNeedsOverwrite.value = true;
|
||||
} else if (error.body) {
|
||||
restoreError.value.generic = error.body.message;
|
||||
} else {
|
||||
restoreError.value.generic = 'Internal error';
|
||||
console.error(error);
|
||||
}
|
||||
restoreBusy.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
restoreBusy.value = false;
|
||||
restoreDialog.value.close();
|
||||
|
||||
window.location.href = '/#/apps';
|
||||
restoreDialog.value.open(archive);
|
||||
}
|
||||
|
||||
async function onDownloadConfig(archive) {
|
||||
@@ -248,45 +103,7 @@ onMounted(async () => {
|
||||
<template>
|
||||
<Section :title="$t('backups.archives.title')">
|
||||
<InputDialog ref="inputDialog"/>
|
||||
<Dialog ref="restoreDialog"
|
||||
:title="$t('backups.restoreArchiveDialog.title')"
|
||||
reject-style="secondary"
|
||||
:reject-label="restoreBusy ? '' : $t('main.dialog.cancel')"
|
||||
:confirm-label="$t(restoreNeedsOverwrite ? 'backups.restoreArchiveDialog.restoreActionOverwrite' : 'backups.restoreArchiveDialog.restoreAction')"
|
||||
:confirm-busy="restoreBusy"
|
||||
:confirm-active="!restoreBusy"
|
||||
@confirm="onRestoreSubmit()"
|
||||
>
|
||||
<p v-html="$t('backups.restoreArchiveDialog.description', { appId: restoreManifest.id, fqdn: restoreFqdn, creationTime: prettyLongDate(restoreArchive.creationTime) })"></p>
|
||||
|
||||
<div class="error-label" v-show="restoreError.generic">{{ restoreError.generic }}</div>
|
||||
<div class="error-label" v-show="restoreError.dnsInUse">{{ restoreError.dnsInUse }}</div>
|
||||
<div class="error-label" v-show="restoreError.port">{{ restoreError.port }}</div>
|
||||
|
||||
<form @submit.prevent="onRestoreSubmit()" autocomplete="off">
|
||||
<fieldset>
|
||||
<FormGroup>
|
||||
<label for="locationInput">{{ $t('app.cloneDialog.location') }}</label>
|
||||
<InputGroup>
|
||||
<TextInput id="locationInput" ref="locationInput" v-model="restoreLocation" style="flex-grow: 1;" />
|
||||
<SingleSelect v-model="restoreDomain" :options="domains" option-label="domain" option-key="domain" />
|
||||
</InputGroup>
|
||||
<div class="warning-label" v-show="restoreDomainProvider === 'noop' || restoreDomainProvider === 'manual'" v-html="$t('appstore.installDialog.manualWarning', { location: ((location ? location + '.' : '') + domain) })"></div>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup v-for="(domain, key) in restoreSecondaryDomains" :key="key">
|
||||
<label :for="'secondaryDomainInput-' + key">{{ domain.title }}</label>
|
||||
<small>{{ domain.description }}</small>
|
||||
<InputGroup>
|
||||
<TextInput :id="'secondaryDomainInput-' + key" v-model="domain.subdomain" :placeholder="$t('appstore.installDialog.locationPlaceholder')" style="flex-grow: 1;" />
|
||||
<SingleSelect v-model="domain.domain" :options="domains" option-label="domain" option-key="domain" />
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
|
||||
<PortBindings v-model:tcp="restoreTcpPorts" v-model:udp="restoreUdpPorts" :error="restoreError" :domain-provider="restoreDomainProvider"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</Dialog>
|
||||
<AppRestoreDialog ref="restoreDialog"/>
|
||||
|
||||
<p v-html="$t('backups.archive.description')"></p>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user