diff --git a/package.json b/package.json index 316dfcee..4293e0e8 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "lint": "vue-cli-service lint --fix" }, "dependencies": { + "axios": "^0.21.1", "connect": "^3.7.0", + "crypto-js": "^4.0.0", "register-service-worker": "^1.6.2", "remedial": "^1.0.8", "serve-static": "^1.14.1", diff --git a/src/components/Configuration/CloudBackupRestore.vue b/src/components/Configuration/CloudBackupRestore.vue index 79625c63..0ba3d3d6 100644 --- a/src/components/Configuration/CloudBackupRestore.vue +++ b/src/components/Configuration/CloudBackupRestore.vue @@ -14,21 +14,30 @@

-

Backup

+

Update Backup

+

Make a Backup

- +
+ Your Backup ID: +
{{ backupId }}
+ + This is used to restore from backups later. + So keep it, along with your password somewhere safe. + +
-

Restore

+

Restore a Backup

+import sha256 from 'crypto-js/sha256'; import Button from '@/components/FormElements/Button'; import Input from '@/components/FormElements/Input'; import IconBackup from '@/assets/interface-icons/config-backup.svg'; import IconRestore from '@/assets/interface-icons/config-restore.svg'; +import { backup } from '@/utils/CloudBackup'; +import { localStorageKeys } from '@/utils/defaults'; export default { name: 'CloudBackupRestore', @@ -65,6 +77,7 @@ export default { backupPassword: '', restorePassword: '', restoreCode: '', + backupId: localStorage[localStorageKeys.BACKUP_ID] || '', }; }, components: { @@ -73,6 +86,44 @@ export default { IconBackup, IconRestore, }, + methods: { + checkPass() { + const savedHash = localStorage[localStorageKeys.BACKUP_HASH] || undefined; + if (!savedHash || savedHash === this.makeHash(this.backupPassword)) { + this.makeBackup(); + } else { + this.showErrorMsg('Incorrect password. Please enter the password you used last time.'); + } + }, + makeBackup() { + backup(this.config, this.backupPassword) + .then((response) => { + if (!response.data || response.data.errorMsg || !response.data.backupId) { + this.showErrorMsg(response.data.errorMsg || 'Error'); + } else { // All clear, no error + this.updateAfterBackup(response.data.backupId); + } + }).catch(() => { + this.showErrorMsg('Unable to process request'); + }); + }, + updateAfterBackup(backupId) { + const hash = this.makeHash(this.backupPassword); + localStorage.setItem(localStorageKeys.BACKUP_ID, backupId); + localStorage.setItem(localStorageKeys.BACKUP_HASH, hash); + this.showSuccessMsg('Backup Completed Succesfully'); + this.backupPassword = ''; + }, + showErrorMsg(errorMsg) { + this.$toasted.show(errorMsg, { className: 'toast-error' }); + }, + showSuccessMsg(msg) { + this.$toasted.show(msg, { className: 'toast-success' }); + }, + makeHash(pass) { + return sha256(pass).toString(); + }, + }, }; @@ -112,8 +163,29 @@ export default { } } + div.results-view { + width: 16rem; + margin: 0.5rem auto; + padding: 0.5rem 0.75rem; + box-sizing: border-box; + border: 1px dashed var(--config-settings-color); + border-radius: var(--curve-factor); + text-align: left; + .backup-id-label, .backup-id-value { + display: inline; + font-size: 1rem; + margin-right: 0.5rem; + } + .backup-id-note { + font-size: 0.8rem; + display: block; + opacity: 0.8; + margin-top: 0.5rem; + } + } + /* Overide form element colors, so that config menu can be themed by user */ - input, button { + input, button, { color: var(--config-settings-color); border: 1px solid var(--config-settings-color); background: none; diff --git a/src/components/FormElements/Button.vue b/src/components/FormElements/Button.vue index c249ca25..4ea452d1 100644 --- a/src/components/FormElements/Button.vue +++ b/src/components/FormElements/Button.vue @@ -1,5 +1,5 @@