import: make the ui work again

This commit is contained in:
Girish Ramakrishnan
2025-07-28 11:45:10 +02:00
parent 89388940ed
commit 373ef5b7e1
6 changed files with 64 additions and 62 deletions
+22 -13
View File
@@ -6,6 +6,7 @@ import { s3like } 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';
import { SECRET_PLACEHOLDER } from '../constants.js';
const appsModel = AppsModel.create();
@@ -18,6 +19,9 @@ const formError = ref({});
const providerConfig = ref({});
const provider = ref('');
const remotePath = ref('');
const format = ref('');
const encryptionPassword = ref('');
const encryptedFilenames = ref(false);
async function onSubmit() {
if (!form.value.reportValidity()) return;
@@ -163,25 +167,23 @@ function onBackupConfigChanged(event) {
reader.onload = function (result) {
if (!result.target || !result.target.result) return console.error('Unable to read backup config');
let config;
let data;
try {
config = JSON.parse(result.target.result);
if (config.provider === 'filesystem') { // this allows a user to upload a backup to server and import easily with an absolute path
config.remotePath = config.backupFolder + '/' + config.remotePath;
delete config.backupFolder;
data = JSON.parse(result.target.result); // 'provider', 'config', 'limits', 'format', 'remotePath', 'encrypted', 'encryptedFilenames'
if (data.provider === 'filesystem') { // this allows a user to upload a backup to server and import easily with an absolute path
data.remotePath = `${data.config.backupFolder}/${data.remotePath}`;
}
} catch (e) {
console.error('Unable to parse backup config', e);
return;
}
provider.value = config.provider;
remotePath.value = config.remotePath;
// we assume property names match here, this does not yet work for gcs keys
Object.keys(config).forEach(function (k) {
providerConfig.value[k] = config[k];
});
provider.value = data.provider;
remotePath.value = data.remotePath;
providerConfig.value = data.config;
format.value = data.format;
encryptionPassword.value = data.encrypted ? SECRET_PLACEHOLDER : '';
encryptedFilenames.value = data.encryptedFilenames;
};
reader.readAsText(event.target.files[0]);
@@ -233,7 +235,14 @@ defineExpose({
<TextInput id="inputRemotePath" v-model="remotePath" required />
</FormGroup>
<BackupProviderForm ref="form" v-model:provider="provider" v-model:provider-config="providerConfig" :form-error="formError" :import-only="true" />
<BackupProviderForm ref="form"
v-model:provider="provider"
v-model:provider-config="providerConfig"
v-model:format="format"
v-model:encryptionPassword="encryptionPassword"
v-model:encryptedFilenames="encryptedFilenames"
:form-error="formError"
:import-only="true" />
</fieldset>
</form>
</div>
@@ -10,6 +10,10 @@ import { mountlike, s3like } from '../utils.js';
const provider = defineModel('provider');
const providerConfig = defineModel('providerConfig');
const format = defineModel('format');
const encryptionPassword = defineModel('encryptionPassword');
const encryptedFilenames = defineModel('encryptedFilenames');
const props = defineProps({
formError: {},
importOnly: {
@@ -292,16 +296,16 @@ onMounted(async () => {
<FormGroup v-if="provider !== 'noop'">
<label for="formatInput">{{ $t('backups.configureBackupStorage.format') }} <sup><a href="https://docs.cloudron.io/backups/#backup-formats" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<SingleSelect id="formatInput" v-model="providerConfig.format" :options="BACKUP_FORMATS" option-label="name" option-key="value" required />
<div class="warning-label" v-show="providerConfig.format === 'rsync' && (s3like(provider) || provider === 'gcs') && !importOnly">{{ $t('backups.configureBackupStorage.s3LikeNote') }} <sup><a href="https://docs.cloudron.io/backups/#amazon-s3" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></div>
<SingleSelect id="formatInput" v-model="format" :options="BACKUP_FORMATS" option-label="name" option-key="value" required />
<div class="warning-label" v-show="format === 'rsync' && (s3like(provider) || provider === 'gcs') && !importOnly">{{ $t('backups.configureBackupStorage.s3LikeNote') }} <sup><a href="https://docs.cloudron.io/backups/#amazon-s3" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></div>
</FormGroup>
<FormGroup v-if="provider !== 'noop'">
<label for="encryptionPassswordInput">{{ $t('backups.configureBackupStorage.encryptionPassword') }} <sup><a href="https://docs.cloudron.io/backups/#encryption" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<TextInput id="encryptionPassswordInput" v-model="providerConfig.encryptionPassword" :placeholder="$t('backups.configureBackupStorage.encryptionPasswordPlaceholder')" />
<TextInput id="encryptionPassswordInput" v-model="encryptionPassword" :placeholder="$t('backups.configureBackupStorage.encryptionPasswordPlaceholder')" />
</FormGroup>
<div class="warning-label" v-show="providerConfig.encryptionPassword && !importOnly">{{ $t('backups.configureBackupStorage.encryptionDescription') }}</div>
<Checkbox v-if="providerConfig.format === 'rsync' && providerConfig.encryptionPassword" v-model="providerConfig.encryptedFilenames" :label="$t(importOnly ? 'backups.configureBackupStorage.encryptedFilenames' : 'backups.configureBackupStorage.encryptFilenames')"/>
<div class="warning-label" v-show="encryptionPassword && !importOnly">{{ $t('backups.configureBackupStorage.encryptionDescription') }}</div>
<Checkbox v-if="format === 'rsync' && encryptionPassword" v-model="encryptedFilenames" :label="$t(importOnly ? 'backups.configureBackupStorage.encryptedFilenames' : 'backups.configureBackupStorage.encryptFilenames')"/>
<p class="actionable" @click="advancedVisible = true" v-if="!advancedVisible && !importOnly">{{ $t('backups.configureBackupStorage.advancedSettings') }}</p>
<div v-if="advancedVisible && !importOnly">
@@ -325,19 +329,19 @@ onMounted(async () => {
</datalist>
</FormGroup>
<FormGroup v-if="providerConfig.format === 'rsync' && provider !== 'noop'">
<FormGroup v-if="format === 'rsync' && provider !== 'noop'">
<label for="syncConcurrencyInput">{{ $t('backups.configureBackupStorage.uploadConcurrency') }}: <b>{{ providerConfig.limits.syncConcurrency }}</b></label>
<div class="small">{{ $t('backups.configureBackupStorage.uploadConcurrencyDescription') }}</div>
<input type="range" id="syncConcurrencyInput" v-model="providerConfig.limits.syncConcurrency" step="10" min="10" max="200" />
</FormGroup>
<FormGroup v-if="providerConfig.format === 'rsync' && (s3like(provider) || provider === 'gcs')">
<FormGroup v-if="format === 'rsync' && (s3like(provider) || provider === 'gcs')">
<label for="downloadConcurrencyInput">{{ $t('backups.configureBackupStorage.downloadConcurrency') }}: <b>{{ providerConfig.limits.downloadConcurrency }}</b></label>
<div class="small">{{ $t('backups.configureBackupStorage.downloadConcurrencyDescription') }}</div>
<input type="range" id="downloadConcurrencyInput" v-model="providerConfig.limits.downloadConcurrency" step="10" min="10" max="200" />
</FormGroup>
<FormGroup v-if="providerConfig.format === 'rsync' && (s3like(provider) || provider === 'gcs')">
<FormGroup v-if="format === 'rsync' && (s3like(provider) || provider === 'gcs')">
<label for="copyConcurrencyInput">{{ $t('backups.configureBackupStorage.copyConcurrency') }}: <b>{{ providerConfig.limits.copyConcurrency }}</b></label>
<div class="small">{{ $t('backups.configureBackupStorage.copyConcurrencyDescription') }}
<span v-show="provider === 'digitalocean-spaces'">{{ $t('backups.configureBackupStorage.copyConcurrencyDigitalOceanNote') }}</span>
+3 -12
View File
@@ -7,18 +7,16 @@ const t = i18n.t;
import { ref, onMounted, useTemplateRef } from 'vue';
import { Icon, Button, Switch, Checkbox, FormGroup, TextInput, TableView, ButtonGroup, Dialog, ProgressBar } from '@cloudron/pankow';
import { prettyLongDate } from '@cloudron/pankow/utils';
import { API_ORIGIN, SECRET_PLACEHOLDER } from '../../constants.js';
import { API_ORIGIN } from '../../constants.js';
import { download } from '../../utils.js';
import AppImportDialog from '../AppImportDialog.vue';
import AppRestoreDialog from '../AppRestoreDialog.vue';
import SettingsItem from '../SettingsItem.vue';
import AppsModel from '../../models/AppsModel.js';
import BackupsModel from '../../models/BackupsModel.js';
import BackupTargetsModel from '../../models/BackupTargetsModel.js';
import TasksModel from '../../models/TasksModel.js';
const appsModel = AppsModel.create();
const backupsModel = BackupsModel.create();
const backupTargetsModel = BackupTargetsModel.create();
const tasksModel = TasksModel.create();
@@ -128,7 +126,6 @@ async function onStopBackup() {
stopBackupBusy.value = false;
}
function onEdit(backup) {
editBusy.value = false;
editBackup.value = backup;
@@ -159,16 +156,10 @@ async function onDownloadConfig(backup) {
const [error, backupTarget] = await backupTargetsModel.get(backup.targetId);
if (error) return console.error(error);
// secrets and tokens already come with placeholder characters we remove them
const tmp = {
remotePath: backup.remotePath,
encrypted: !!backupTarget.password, // we add this just to help the import UI
encryptedFilenames: !!backupTarget.encryptedFilenames
remotePath: backup.remotePath
};
console.log(backupTarget);
for (const k of ['provider', 'config', 'limits', 'format']) {
for (const k of ['provider', 'config', 'limits', 'format', 'encrypted', 'encryptedFilenames']) {
tmp[k] = backupTarget[k];
}