199 lines
No EOL
8.1 KiB
Vue
199 lines
No EOL
8.1 KiB
Vue
<template>
|
|
<div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-0">Backup Account Key</h5>
|
|
<form>
|
|
<div class="mb-3">
|
|
<label class="form-label d-block">Backup your account key</label>
|
|
<button type="button" class="btn btn-primary" @click="exportKey">
|
|
Backup
|
|
</button>
|
|
<br>
|
|
<small>Backup your account key to restore your account in case
|
|
of loss</small>
|
|
<br>
|
|
<p>{{ localUserIdentityRecord }}</p>
|
|
|
|
<vue-qrcode :value="'foo'" tag="svg" :size="200" :options="{errorCorrectionLevel: 'H'}"></vue-qrcode>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-0">Export</h5>
|
|
<form>
|
|
<div class="mb-3">
|
|
<label class="form-label
|
|
d-block">Download your data</label>
|
|
<button type="button" class="btn btn-primary" @click="exportData">
|
|
Download
|
|
</button>
|
|
<br>
|
|
<small>Download all your data including photos, videos,
|
|
comments, profile information and more</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label
|
|
d-block">Delete your data</label>
|
|
<button type="button" class="btn btn-danger" @click="deleteData">
|
|
Delete
|
|
</button>
|
|
<br>
|
|
<small>Delete all your data including photos, videos,
|
|
comments, profile information and more</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title mb-0">Import</h5>
|
|
<form>
|
|
<div class="mb-3">
|
|
<label class="form-label
|
|
d-block">Import data</label>
|
|
<div class="btn-group">
|
|
<input type="file" class="form-control" id="inputFile"
|
|
accept=".zip" @change="chooseFile">
|
|
<button type="button" class="btn btn-primary" @click="importData"
|
|
:disabled="!selectedFile">
|
|
Import
|
|
</button>
|
|
</div>
|
|
<br>
|
|
<small>Import data from backup or other instances</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {mapActions, mapGetters} from 'vuex';
|
|
import router from "@/router";
|
|
|
|
//import VueQrcode from '@chenfengyuan/vue-qrcode';
|
|
|
|
export default {
|
|
name: 'Data',
|
|
components: {},
|
|
data: () => ({
|
|
selectedFile: null,
|
|
localUserIdentityRecord: null
|
|
}),
|
|
computed: {
|
|
...mapGetters(['signAuth'])
|
|
},
|
|
methods: {
|
|
...mapActions(['getHomeServers', 'userIdentityRecord']),
|
|
chooseFile(event) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
this.selectedFile = file;
|
|
}
|
|
},
|
|
async deleteData() {
|
|
if (!confirm('Are you sure you want to permanently delete all your data (inventory, locations, ' +
|
|
'settings, friends and files)? Your account itself will stay - this cannot be undone.')) {
|
|
return;
|
|
}
|
|
try {
|
|
const servers = await this.getHomeServers();
|
|
const response = await servers.delete(this.signAuth, '/api/account_data/');
|
|
if (!response || !response.ok) {
|
|
const errorBody = response ? await response.json().catch(() => ({})) : {};
|
|
alert('Data deletion failed: ' + (errorBody.detail || response?.statusText || 'unknown error'));
|
|
return;
|
|
}
|
|
const summary = await response.json().catch(() => ({}));
|
|
alert('All your data has been deleted: ' +
|
|
`${summary.inventory_items || 0} inventory items, ` +
|
|
`${summary.locations || 0} locations, ` +
|
|
`${summary.settings || 0} settings, ` +
|
|
`${summary.friends || 0} friends, ` +
|
|
`${summary.files || 0} files.`);
|
|
} catch (error) {
|
|
console.error('Data deletion failed', error);
|
|
alert('Data deletion failed: ' + error);
|
|
}
|
|
},
|
|
exportKey() {
|
|
const key = this.userIdentityRecord;
|
|
},
|
|
fileToBase64(file) {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
const buffer = reader.result;
|
|
if (!(buffer instanceof ArrayBuffer)) {
|
|
reject(new Error('Could not read file'));
|
|
return;
|
|
}
|
|
const data = new Uint8Array(buffer);
|
|
const base64 = btoa(data.reduce((acc, byte) => acc + String.fromCharCode(byte), ''));
|
|
resolve(base64);
|
|
};
|
|
reader.onerror = (error) => reject(error);
|
|
reader.readAsArrayBuffer(file);
|
|
});
|
|
},
|
|
async importData() {
|
|
if (!this.selectedFile) {
|
|
alert('Please select a file to import');
|
|
return;
|
|
}
|
|
try {
|
|
const base64 = await this.fileToBase64(this.selectedFile);
|
|
const servers = await this.getHomeServers();
|
|
const summary = await servers.post(this.signAuth, '/api/import/', {zip: base64});
|
|
if (summary && summary.detail) {
|
|
alert('Data import failed: ' + summary.detail);
|
|
return;
|
|
}
|
|
alert('Data imported successfully: ' +
|
|
`${summary.profile ? 'profile, ' : ''}` +
|
|
`${summary.settings || 0} settings, ` +
|
|
`${summary.inventory_items || 0} inventory items, ` +
|
|
`${summary.friends || 0} friends, ` +
|
|
`${summary.locations || 0} locations, ` +
|
|
`${summary.files || 0} files.`);
|
|
this.selectedFile = null;
|
|
const fileInput = document.getElementById('inputFile');
|
|
if (fileInput) {
|
|
fileInput.value = '';
|
|
}
|
|
} catch (error) {
|
|
console.error('Data import failed', error);
|
|
alert('Data import failed: ' + error);
|
|
}
|
|
},
|
|
async exportData() {
|
|
const servers = await this.getHomeServers();
|
|
const data = await servers.getRaw(this.signAuth, '/api/export/');
|
|
const blob = new Blob([await data.blob()], {type: 'application/zip'});
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
const date_str = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '').replace(/:/g, '-');
|
|
a.download = 'toolshed_data_' + date_str + '.zip';
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
|
|
}
|
|
},
|
|
mounted() {
|
|
this.userIdentityRecord({}).then((data) => {
|
|
this.localUserIdentityRecord = data;
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
|
|
</style> |