diff --git a/dashboard/public/translation/en.json b/dashboard/public/translation/en.json index 860e50925..9f77b3e90 100644 --- a/dashboard/public/translation/en.json +++ b/dashboard/public/translation/en.json @@ -1771,7 +1771,9 @@ "title": "Restore {{ app }}", "description": "This will restore this app to the data from {{ creationTime }}.", "warning": "Any data generated between now and the last known backup will be irrevocably lost. It is recommended to create a backup of the current data before attempting a restore.", - "restoreAction": "Restore" + "restoreAction": "Restore", + "cloneAction": "Clone", + "cloneActionOverwrite": "Clone and overwrite DNS" }, "cloneDialog": { "title": "Clone {{ app }}", diff --git a/dashboard/public/translation/nl.json b/dashboard/public/translation/nl.json index 0bb41127f..a74b6e711 100644 --- a/dashboard/public/translation/nl.json +++ b/dashboard/public/translation/nl.json @@ -1281,7 +1281,8 @@ "archiveDialog": { "title": "Archief {{app}}", "description": "Hiermee wordt de app gedeïnstalleerd en wordt de laatste app backup van {{date}} bewaard in het App Archief." - } + }, + "updateAvailableTooltip": "Update beschikbaar" }, "network": { "title": "Netwerk", diff --git a/dashboard/src/components/AppRestoreDialog.vue b/dashboard/src/components/AppRestoreDialog.vue index 50fced548..c94131143 100644 --- a/dashboard/src/components/AppRestoreDialog.vue +++ b/dashboard/src/components/AppRestoreDialog.vue @@ -6,21 +6,24 @@ import { ref, useTemplateRef, computed } from 'vue'; import { InputGroup, FormGroup, TextInput, SingleSelect, Dialog } from 'pankow'; import { prettyLongDate } from 'pankow/utils'; import PortBindings from '../components/PortBindings.vue'; +import AppsModel from '../models/AppsModel.js'; import ArchivesModel from '../models/ArchivesModel.js'; import DomainsModel from '../models/DomainsModel.js'; +const appsModel = AppsModel.create(); const archivesModel = ArchivesModel.create(); const domainsModel = DomainsModel.create(); -const restoreDialog = useTemplateRef('restoreDialog'); +const appId = ref(null); +const dialog = useTemplateRef('dialog'); const restoreArchive = ref({}); -const restoreFqdn = ref(''); +const fqdn = ref(''); const restoreManifest = ref({}); const restoreLocation = ref(''); const restoreDomain = ref(''); -const restoreNeedsOverwrite = ref(false); -const restoreBusy = ref(false); -const restoreError = ref({}); +const needsOverwrite = ref(false); +const busy = ref(false); +const formError = ref({}); const restoreTcpPorts = ref({}); const restoreUdpPorts = ref({}); const domains = ref([]); @@ -30,10 +33,85 @@ const restoreDomainProvider = computed(() => { return tmp ? tmp.provider : ''; }); +async function onSubmit() { + busy.value = true; + formError.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: needsOverwrite.value, + }; + + // we could come from restoring an app archive or cloning an app from a backup + if (appId.value) { + console.log('we are cloning!!!', restoreArchive.value, appId.value); + data.backupId = restoreArchive.value.id; + const [error] = await appsModel.clone(appId.value, data); + if (error) { + if (error.type === 'externally_exists') { + formError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.'; + needsOverwrite.value = true; + } else if (error.body) { + formError.value.generic = error.body.message; + } else { + formError.value.generic = 'Internal error'; + console.error(error); + } + busy.value = false; + return; + } + } else { + const [error] = await archivesModel.restore(restoreArchive.value.id, data); + if (error) { + if (error.type === 'externally_exists') { + formError.value.dnsInUse = 'Some DNS records exist. Submit again to overwrite.'; + needsOverwrite.value = true; + } else if (error.body) { + formError.value.generic = error.body.message; + } else { + formError.value.generic = 'Internal error'; + console.error(error); + } + busy.value = false; + return; + } + } + + busy.value = false; + dialog.value.close(); + + window.location.href = '/#/apps'; +} + defineExpose({ - async open(archive) { - restoreBusy.value = false; - restoreError.value = {}; + async open(archive, cloneFromAppId = null) { + appId.value = cloneFromAppId; + busy.value = false; + formError.value = {}; const [error, result] = await domainsModel.list(); if (error) return console.error(error); @@ -54,9 +132,9 @@ defineExpose({ 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; + needsOverwrite.value = false; restoreArchive.value = archive; - restoreFqdn.value = archive.appConfig?.fqdn || '-'; + fqdn.value = archive.appConfig?.fqdn || '-'; restoreManifest.value = manifest; const httpPorts = manifest.httpPorts || {}; @@ -105,83 +183,30 @@ defineExpose({ } } - restoreDialog.value.open(); + dialog.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'; -} -