Support gcs account key upload for backups
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
|
||||
import { ref, useTemplateRef, onMounted, computed, watch } from 'vue';
|
||||
import { Dialog, Dropdown, FormGroup, TextInput, Checkbox, PasswordInput, NumberInput } from 'pankow';
|
||||
import { Button, InputGroup, Dialog, Dropdown, FormGroup, TextInput, Checkbox, PasswordInput, NumberInput } from 'pankow';
|
||||
import { prettyBinarySize } from 'pankow/utils';
|
||||
// TODO REGIONS_UPCLOUD is not used??
|
||||
import { BACKUP_FORMATS, STORAGE_PROVIDERS, REGIONS_CONTABO, REGIONS_VULTR, REGIONS_UPCLOUD, REGIONS_IONOS, REGIONS_OVH, REGIONS_LINODE, REGIONS_SCALEWAY, REGIONS_EXOSCALE, REGIONS_DIGITALOCEAN, REGIONS_HETZNER, REGIONS_WASABI, REGIONS_S3 } from '../constants.js';
|
||||
@@ -28,7 +28,6 @@ const formError = ref({});
|
||||
|
||||
const blockDevices = ref([]);
|
||||
|
||||
// TODO gcs provider needs some special treatment
|
||||
const advancedVisible = ref(false);
|
||||
const busy = ref(false);
|
||||
const provider = ref('');
|
||||
@@ -62,6 +61,38 @@ const prefix = ref('');
|
||||
const region = ref('');
|
||||
const accessKeyId = ref('');
|
||||
const secretAccessKey = ref('');
|
||||
const gcsKeyFileName = ref('');
|
||||
const gcsProjectId = ref('');
|
||||
const gcsKeyContentJson = ref(null);
|
||||
|
||||
function onGcsKeyChange(event) {
|
||||
delete formError.value.gcs;
|
||||
|
||||
const fr = new FileReader();
|
||||
|
||||
fr.onload = () => {
|
||||
// validate input file
|
||||
try {
|
||||
const keyJson = JSON.parse(fr.result);
|
||||
if (!keyJson.project_id) throw new Error('project_id field missing in JSON key file');
|
||||
if (!keyJson.client_email) throw new Error('client_email field missing in JSON key file');
|
||||
if (!keyJson.private_key) throw new Error('private_key field missing in JSON key file');
|
||||
|
||||
gcsProjectId.value = keyJson.project_id;
|
||||
gcsKeyContentJson.value = keyJson;
|
||||
} catch (e) {
|
||||
if (e.name === 'SyntaxError') formError.value.gcs = 'Invalid JSON';
|
||||
else formError.value.gcs = e.message;
|
||||
|
||||
gcsKeyFileName.value = '';
|
||||
gcsProjectId.value = '';
|
||||
gcsKeyContentJson.value = null;
|
||||
}
|
||||
};
|
||||
fr.readAsText(event.target.files[0]);
|
||||
|
||||
gcsKeyFileName.value = event.target.files[0].name;
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
if (!isValid.value) return;
|
||||
@@ -161,6 +192,17 @@ async function onSubmit() {
|
||||
data.backupFolder = backupFolder.value;
|
||||
data.noHardlinks = !useHardlinks.value;
|
||||
data.preserveAttributes = true;
|
||||
} else if (data.provider === 'gcs') {
|
||||
// TODO we should probably allow to change the config without reuploading a new .json
|
||||
if (gcsKeyContentJson.value) return;
|
||||
|
||||
data.bucket = bucket.value;
|
||||
data.prefix = prefix.value;
|
||||
data.projectId = gcsKeyContentJson.value.project_id;
|
||||
data.credentials = {
|
||||
client_email: gcsKeyContentJson.value.client_email,
|
||||
private_key: gcsKeyContentJson.value.private_key
|
||||
};
|
||||
}
|
||||
|
||||
const limits = {
|
||||
@@ -219,7 +261,6 @@ const isValid = computed(() => {
|
||||
});
|
||||
|
||||
watch(provider, (newProvider) => {
|
||||
console.log('provider change', newProvider)
|
||||
if (newProvider === 'scaleway-objectstorage') {
|
||||
// scaleway only supports 1000 parts per object (https://www.scaleway.com/en/docs/s3-multipart-upload/)
|
||||
if (parseInt(uploadPartSize.value) < 100 * 1024 * 1024) uploadPartSize.value = 100 * 1024 * 1024;
|
||||
@@ -273,6 +314,9 @@ defineExpose({
|
||||
region.value = result.region;
|
||||
accessKeyId.value = result.accessKeyId;
|
||||
secretAccessKey.value = result.secretAccessKey;
|
||||
gcsProjectId.value = result.projectId || '';
|
||||
gcsKeyFileName.value = '';
|
||||
gcsKeyContentJson.value = null;
|
||||
|
||||
dialog.value.open();
|
||||
}
|
||||
@@ -446,25 +490,23 @@ defineExpose({
|
||||
<TextInput id="accessKeyInput" v-model="secretAccessKey" required />
|
||||
</FormGroup>
|
||||
|
||||
<!-- <div class="form-group" ng-class="{ 'has-error': configureBackup.error.gcsKeyInput }" ng-show="configureBackup.provider === 'gcs'">
|
||||
<label class="control-label" for="gcsKeyInput">{{ 'backups.configureBackupStorage.gcsServiceKey' | tr }}</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="file" id="gcsKeyFileInput" style="display:none"/>
|
||||
<input type="text" class="form-control" placeholder="Service Account Key" ng-model="configureBackup.gcsKey.keyFileName" id="gcsKeyInput" name="cert" onclick="getElementById('gcsKeyFileInput').click();" style="cursor: pointer;" ng-disabled="configureBackup.busy" ng-required="configureBackup.provider === 'gcs'">
|
||||
<span class="input-group-addon">
|
||||
<i class="fa fa-upload" onclick="getElementById('gcsKeyFileInput').click();"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<FormGroup v-if="provider === 'gcs'">
|
||||
<input type="file" id="gcsKeyFileInput" style="display:none" @change="onGcsKeyChange"/>
|
||||
<label for="gcsKeyInput">{{ $t('backups.configureBackupStorage.gcsServiceKey') }}{{ gcsProjectId ? ` - project: ${gcsProjectId}` : '' }}</label>
|
||||
<InputGroup>
|
||||
<TextInput readonly required style="flex-grow: 1" v-model="gcsKeyFileName" placeholder="Service Account Key" onclick="document.getElementById('gcsKeyFileInput').click();"/>
|
||||
<Button tool icon="fa fa-upload" onclick="document.getElementById('gcsKeyFileInput').click();"/>
|
||||
</InputGroup>
|
||||
<div class="has-error" v-show="formError.gcs">{{ formError.gcs }}</div>
|
||||
</FormGroup>
|
||||
|
||||
<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>
|
||||
<p class="small text-info" v-show="format !== config.format">{{ $t('backups.configureBackupStorage.formatChangeNote') }}</p>
|
||||
<p class="small text-info" v-show="format === 'rsync' && (s3like(provider) || provider === 'gcs')">{{ $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></p>
|
||||
<select class="form-control" id="formatInput" v-model="format">
|
||||
<option v-for="f in BACKUP_FORMATS" :value="f.value" :key="f.value">{{ f.name }}</option>
|
||||
</select>
|
||||
<p class="small text-info" v-show="format !== config.format">{{ $t('backups.configureBackupStorage.formatChangeNote') }}</p>
|
||||
<p class="small text-info" v-show="format === 'rsync' && (s3like(provider) || provider === 'gcs')">{{ $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></p>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup v-if="provider !== 'noop'">
|
||||
|
||||
Reference in New Issue
Block a user