diff --git a/dashboard/src/components/AppImportDialog.vue b/dashboard/src/components/AppImportDialog.vue
index 03aaec96e..f4efc42ee 100644
--- a/dashboard/src/components/AppImportDialog.vue
+++ b/dashboard/src/components/AppImportDialog.vue
@@ -2,7 +2,7 @@
import { ref, useTemplateRef } from 'vue';
import { Dialog, FormGroup, TextInput, PasswordInput, Checkbox } from '@cloudron/pankow';
-import { s3like, mountlike } from '../utils.js';
+import { s3like, mountlike, parseFullBackupPath } from '../utils.js';
import BackupProviderForm from './BackupProviderForm.vue';
import AppsModel from '../models/AppsModel.js';
import { REGIONS_CONTABO, REGIONS_VULTR, REGIONS_IONOS, REGIONS_OVH, REGIONS_LINODE, REGIONS_SCALEWAY, REGIONS_WASABI } from '../constants.js';
@@ -17,7 +17,7 @@ const busy = ref(false);
const formError = ref({});
const providerConfig = ref({});
const provider = ref('');
-const remotePath = ref('');
+const fullPath = ref('');
const format = ref('');
const encrypted = ref(false);
const encryptionPasswordHint = ref('');
@@ -30,15 +30,17 @@ async function onSubmit() {
formError.value = {};
busy.value = true;
- let backupPath = remotePath.value;
const config = {};
+ const { prefix, remotePath } = parseFullBackupPath(fullPath.value);
+
// only set provider specific fields, this will clear them in the db
if (s3like(provider.value)) {
config.bucket = providerConfig.value.bucket;
config.prefix = providerConfig.value.prefix;
config.accessKeyId = providerConfig.value.accessKeyId;
config.secretAccessKey = providerConfig.value.secretAccessKey;
+ config.prefix = prefix;
if (providerConfig.value.endpoint) config.endpoint = providerConfig.value.endpoint;
@@ -85,7 +87,7 @@ async function onSubmit() {
config.signatureVersion = 'v4';
}
} else if (mountlike(provider.value)) {
- config.prefix = providerConfig.value.prefix;
+ config.prefix = prefix;
config.noHardlinks = !providerConfig.value.useHardlinks;
config.mountOptions = {};
@@ -113,21 +115,19 @@ async function onSubmit() {
config.preserveAttributes = !!providerConfig.value.preserveAttributes;
}
} else if (provider.value === 'filesystem') {
- const parts = remotePath.value.split('/');
- backupPath = parts.pop() || parts.pop(); // removes any trailing slash. this is basename()
- config.backupDir = parts.join('/'); // this is dirname()
+ config.backupDir = prefix;
} else if (provider.value === 'gcs') {
config.bucket = providerConfig.value.bucket;
- config.prefix = providerConfig.value.prefix;
config.projectId = providerConfig.value.projectId;
config.credentials = providerConfig.value.credentials;
+ config.prefix = prefix;
}
const data = {
format: format.value,
provider: provider.value,
- config: config,
- remotePath: backupPath
+ config,
+ remotePath
};
if (encrypted.value) {
@@ -197,7 +197,7 @@ function onBackupConfigChanged(event) {
}
provider.value = data.provider;
- remotePath.value = data.config.prefix ? `${data.config.prefix}/${data.remotePath}` : data.remotePath;
+ fullPath.value = data.config.prefix ? `${data.config.prefix}/${data.remotePath}` : data.remotePath;
providerConfig.value = data.config;
format.value = data.format;
encrypted.value = !!data.encrypted;
@@ -220,7 +220,7 @@ defineExpose({
formError.value = {};
provider.value = '';
providerConfig.value = {};
- remotePath.value = '';
+ fullPath.value = '';
encrypted.value = false;
encryptionPassword.value = '';
encryptedFilenames.value = false;
@@ -265,7 +265,7 @@ defineExpose({
-
+
timestampRegex.test(p) || p === 'snapshot');
+
+ let remotePath, prefix;
+ if (idx === -1) {
+ remotePath = parts.pop() || parts.pop(); // if fs+rsync there may be a trailing slash, so this removes it. this is basename()
+ prefix = parts.join('/'); // this is dirname()
+ } else {
+ prefix = parts.slice(0, idx).join('/');
+ remotePath = parts.slice(idx).join('/');
+ }
+
+ return { prefix, remotePath };
+}
+
// named exports
export {
prettyRelayProviderName,
@@ -757,7 +776,8 @@ export {
getColor,
prettySchedule,
parseSchedule,
- prettySiteLocation
+ prettySiteLocation,
+ parseFullBackupPath
};
// default export
@@ -778,5 +798,6 @@ export default {
getColor,
prettySchedule,
parseSchedule,
- prettySiteLocation
+ prettySiteLocation,
+ parseFullBackupPath
};
diff --git a/dashboard/src/views/RestoreView.vue b/dashboard/src/views/RestoreView.vue
index ab1b0b959..b819d8000 100644
--- a/dashboard/src/views/RestoreView.vue
+++ b/dashboard/src/views/RestoreView.vue
@@ -4,7 +4,7 @@ import { ref, onMounted, useTemplateRef } from 'vue';
import { Notification, Button, SingleSelect, FormGroup, PasswordInput, TextInput, Checkbox } from '@cloudron/pankow';
import { copyToClipboard } from '@cloudron/pankow/utils';
import { REGIONS_CONTABO, REGIONS_VULTR, REGIONS_IONOS, REGIONS_OVH, REGIONS_LINODE, REGIONS_SCALEWAY, REGIONS_WASABI } from '../constants.js';
-import { redirectIfNeeded, mountlike, s3like } from '../utils.js';
+import { redirectIfNeeded, mountlike, s3like, parseFullBackupPath } from '../utils.js';
import ProvisionModel from '../models/ProvisionModel.js';
import BackupProviderForm from '../components/BackupProviderForm.vue';
import Whirlpool from '../components/Whirlpool.vue';
@@ -27,7 +27,7 @@ const progressMessage = ref('');
const taskMinutesActive = ref(0);
const provider = ref('');
const providerConfig = ref({});
-const remotePath = ref('');
+const fullPath = ref('');
const format = ref('');
const encrypted = ref(false);
const encryptionPasswordHint = ref('');
@@ -87,21 +87,21 @@ async function onSubmit() {
busy.value = true;
formError.value = {};
- if (remotePath.value.indexOf('/') === -1) {
+ if (fullPath.value.indexOf('/') === -1) {
error.value.generic = 'Backup id must include the directory path';
error.value.remotePath = true;
busy.value = false;
return;
}
- if (remotePath.value.indexOf('box') === -1) {
+ if (fullPath.value.indexOf('box') === -1) {
error.value.generic = 'Backup id must contain "box"';
error.value.remotePath = true;
busy.value = false;
return;
}
- const version = remotePath.value.match(/_v(\d+.\d+.\d+)/);
+ const version = fullPath.value.match(/_v(\d+.\d+.\d+)/);
if (!version) {
formError.value.generic = 'Backup id is missing version information';
formError.value.remotePath = true;
@@ -109,11 +109,12 @@ async function onSubmit() {
return;
}
- const config = {}; // filled below
+ const config = {};
+ const { prefix, remotePath } = parseFullBackupPath(fullPath.value);
if (s3like(provider.value)) {
config.endpoint = providerConfig.value.endpoint;
- config.prefix = providerConfig.value.prefix;
+ config.prefix = prefix;
config.bucket = providerConfig.value.bucket;
config.accessKeyId = providerConfig.value.accessKeyId;
config.secretAccessKey = providerConfig.value.secretAccessKey;
@@ -160,7 +161,7 @@ async function onSubmit() {
config.signatureVersion = 'v4';
}
} else if (mountlike(provider.value)) {
- config.prefix = providerConfig.value.prefix;
+ config.prefix = prefix;
config.noHardlinks = !providerConfig.value.useHardlinks;
config.mountOptions = {};
@@ -188,12 +189,12 @@ async function onSubmit() {
config.preserveAttributes = !!providerConfig.value.preserveAttributes;
}
} else if (provider.value === 'filesystem') {
- config.backupDir = providerConfig.value.backupDir;
+ config.backupDir = prefix;
config.noHardlinks = !providerConfig.value.useHardlinks;
config.preserveAttributes = true;
} else if (provider.value === 'gcs') {
config.bucket = providerConfig.value.bucket;
- config.prefix = providerConfig.value.prefix;
+ config.prefix = prefix;
config.projectId = providerConfig.value.projectId;
config.credentials = providerConfig.value.credentials;
}
@@ -204,7 +205,7 @@ async function onSubmit() {
config,
format: format.value,
},
- remotePath: remotePath.value,
+ remotePath,
version: version ? version[1] : '',
ipv4Config: {
provider: ipv4Provider.value,
@@ -274,7 +275,7 @@ function onBackupConfigChanged(event) {
}
provider.value = data.provider;
- remotePath.value = data.config.prefix ? `${data.config.prefix}/${data.remotePath}` : data.remotePath;
+ fullPath.value = data.config.prefix ? `${data.config.prefix}/${data.remotePath}` : data.remotePath;
providerConfig.value = data.config;
format.value = data.format;
encrypted.value = !!data.encrypted;
@@ -351,7 +352,7 @@ onMounted(async () => {
-
+