mirror of
https://github.com/Lissy93/dashy.git
synced 2024-11-27 10:26:00 +03:00
Adds backup functionality
This commit is contained in:
parent
8ffbfb8123
commit
44b2594dfa
@ -8,7 +8,9 @@
|
|||||||
"lint": "vue-cli-service lint --fix"
|
"lint": "vue-cli-service lint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
"connect": "^3.7.0",
|
"connect": "^3.7.0",
|
||||||
|
"crypto-js": "^4.0.0",
|
||||||
"register-service-worker": "^1.6.2",
|
"register-service-worker": "^1.6.2",
|
||||||
"remedial": "^1.0.8",
|
"remedial": "^1.0.8",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
|
@ -14,21 +14,30 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section backup-section">
|
<div class="section backup-section">
|
||||||
<h3>Backup</h3>
|
<h3 v-if="backupId">Update Backup</h3>
|
||||||
|
<h3 v-else>Make a Backup</h3>
|
||||||
<Input
|
<Input
|
||||||
v-model="backupPassword"
|
v-model="backupPassword"
|
||||||
name="backup-password"
|
name="backup-password"
|
||||||
label="Choose a Password"
|
:label="backupId ? 'Enter your Password' : 'Choose a Password'"
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
<Button>
|
<Button :click="checkPass">
|
||||||
<template v-slot:text>Backup</template>
|
<template v-slot:text>{{backupId ? 'Update Backup' : 'Backup'}}</template>
|
||||||
<template v-slot:icon><IconBackup /></template>
|
<template v-slot:icon><IconBackup /></template>
|
||||||
</Button>
|
</Button>
|
||||||
|
<div class="results-view" v-if="backupId">
|
||||||
|
<span class="backup-id-label">Your Backup ID: </span>
|
||||||
|
<pre class="backup-id-value">{{ backupId }}</pre>
|
||||||
|
<span class="backup-id-note">
|
||||||
|
This is used to restore from backups later.
|
||||||
|
So keep it, along with your password somewhere safe.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section restore-section">
|
<div class="section restore-section">
|
||||||
<h3>Restore</h3>
|
<h3>Restore a Backup</h3>
|
||||||
<Input
|
<Input
|
||||||
v-model="restoreCode"
|
v-model="restoreCode"
|
||||||
name="restore-code"
|
name="restore-code"
|
||||||
@ -50,10 +59,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import sha256 from 'crypto-js/sha256';
|
||||||
import Button from '@/components/FormElements/Button';
|
import Button from '@/components/FormElements/Button';
|
||||||
import Input from '@/components/FormElements/Input';
|
import Input from '@/components/FormElements/Input';
|
||||||
import IconBackup from '@/assets/interface-icons/config-backup.svg';
|
import IconBackup from '@/assets/interface-icons/config-backup.svg';
|
||||||
import IconRestore from '@/assets/interface-icons/config-restore.svg';
|
import IconRestore from '@/assets/interface-icons/config-restore.svg';
|
||||||
|
import { backup } from '@/utils/CloudBackup';
|
||||||
|
import { localStorageKeys } from '@/utils/defaults';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CloudBackupRestore',
|
name: 'CloudBackupRestore',
|
||||||
@ -65,6 +77,7 @@ export default {
|
|||||||
backupPassword: '',
|
backupPassword: '',
|
||||||
restorePassword: '',
|
restorePassword: '',
|
||||||
restoreCode: '',
|
restoreCode: '',
|
||||||
|
backupId: localStorage[localStorageKeys.BACKUP_ID] || '',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@ -73,6 +86,44 @@ export default {
|
|||||||
IconBackup,
|
IconBackup,
|
||||||
IconRestore,
|
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();
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -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 */
|
/* Overide form element colors, so that config menu can be themed by user */
|
||||||
input, button {
|
input, button, {
|
||||||
color: var(--config-settings-color);
|
color: var(--config-settings-color);
|
||||||
border: 1px solid var(--config-settings-color);
|
border: 1px solid var(--config-settings-color);
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button>
|
<button @click="click()">
|
||||||
<slot name="text"></slot>
|
<slot name="text"></slot>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
</button>
|
</button>
|
||||||
@ -11,6 +11,7 @@ export default {
|
|||||||
name: 'Button',
|
name: 'Button',
|
||||||
props: {
|
props: {
|
||||||
text: String,
|
text: String,
|
||||||
|
click: Function,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</modal>
|
</modal>
|
||||||
|
|
||||||
<!-- Modal for cloud backup and restore options -->
|
<!-- Modal for cloud backup and restore options -->
|
||||||
<modal :name="modalNames.CLOUD_BACKUP" :resizable="true" width="60%" height="60%"
|
<modal :name="modalNames.CLOUD_BACKUP" :resizable="true" width="65%" height="60%"
|
||||||
@closed="$emit('modalChanged', false)">
|
@closed="$emit('modalChanged', false)">
|
||||||
<CloudBackupRestore :config="combineConfig()" />
|
<CloudBackupRestore :config="combineConfig()" />
|
||||||
</modal>
|
</modal>
|
||||||
|
@ -5,6 +5,16 @@
|
|||||||
--background: #0b1021; // Page background
|
--background: #0b1021; // Page background
|
||||||
--background-darker: #05070e; // Used for navigation bar, footer and fills
|
--background-darker: #05070e; // Used for navigation bar, footer and fills
|
||||||
|
|
||||||
|
/* Action Colors */
|
||||||
|
--info: #04e4f4;
|
||||||
|
--success: #20e253;
|
||||||
|
--warning: #f6f000;
|
||||||
|
--danger: #f80363;
|
||||||
|
--neutral: #272f4d;
|
||||||
|
|
||||||
|
--white: #fff;
|
||||||
|
--black: #000;
|
||||||
|
|
||||||
/* Modified Colors */
|
/* Modified Colors */
|
||||||
--item-group-background: #0b1021cc;
|
--item-group-background: #0b1021cc;
|
||||||
--medium-grey: #5e6474;
|
--medium-grey: #5e6474;
|
||||||
@ -16,6 +26,11 @@
|
|||||||
--transparent-50: #00000080;
|
--transparent-50: #00000080;
|
||||||
--transparent-30: #0000004d;
|
--transparent-30: #0000004d;
|
||||||
|
|
||||||
|
/* Semi-Transparent White*/
|
||||||
|
--transparent-white-70: #ffffffb3;
|
||||||
|
--transparent-white-50: #ffffff80;
|
||||||
|
--transparent-white-30: #ffffff4d;
|
||||||
|
|
||||||
/* Other Variables */
|
/* Other Variables */
|
||||||
--outline-color: none;
|
--outline-color: none;
|
||||||
--curve-factor: 5px; // Border radius for most components
|
--curve-factor: 5px; // Border radius for most components
|
||||||
|
@ -20,6 +20,14 @@ h1, h2, h3, h4, h5 {
|
|||||||
font-family: 'Inconsolata', sans-serif;
|
font-family: 'Inconsolata', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bold { font-weight: bold; }
|
||||||
|
.light { font-weight: lighter; }
|
||||||
|
.text-left { text-align: left;}
|
||||||
|
.text-right { text-align: right;}
|
||||||
|
.text-center { text-align: center;}
|
||||||
|
.horizontal-center { margin: 0 auto; }
|
||||||
|
.border-box { box-sizing: border-box; }
|
||||||
|
|
||||||
/* Overiding styles for the global toast component */
|
/* Overiding styles for the global toast component */
|
||||||
.toast-message {
|
.toast-message {
|
||||||
background: var(--toast-background) !important;
|
background: var(--toast-background) !important;
|
||||||
@ -29,4 +37,15 @@ h1, h2, h3, h4, h5 {
|
|||||||
font-size: 1.25rem !important;
|
font-size: 1.25rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast-error {
|
||||||
|
background: var(--danger) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
.toast-success {
|
||||||
|
background: var(--success) !important;
|
||||||
|
color: var(--white) !important;
|
||||||
|
font-size: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import sha256 from 'crypto-js/sha256';
|
import sha256 from 'crypto-js/sha256';
|
||||||
import aes from 'crypto-js/aes';
|
import aes from 'crypto-js/aes';
|
||||||
import Base64 from 'crypto-js/enc-base64';
|
|
||||||
import Hex from 'crypto-js/enc-hex';
|
|
||||||
import Utf8 from 'crypto-js/enc-utf8';
|
import Utf8 from 'crypto-js/enc-utf8';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const ENDPOINT = 'https://dashy-sync-service.as93.net/';
|
||||||
|
|
||||||
/* Stringify, encrypt and encode data for transmission */
|
/* Stringify, encrypt and encode data for transmission */
|
||||||
const encryptData = (data, password) => {
|
const encryptData = (data, password) => {
|
||||||
@ -22,11 +23,16 @@ const makeSubHash = (pass) => sha256(pass).toString().slice(0, 14);
|
|||||||
|
|
||||||
/* Makes the backup */
|
/* Makes the backup */
|
||||||
export const backup = (data, password) => {
|
export const backup = (data, password) => {
|
||||||
// const subHash = makeSubHash(password);
|
return axios.post(ENDPOINT, {
|
||||||
const encryptedData = encryptData(data, password);
|
userData: encryptData(data, password),
|
||||||
console.log(encryptedData);
|
subHash: makeSubHash(password),
|
||||||
console.log(decryptData(encryptedData, password));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Restores the backup */
|
/* Restores the backup */
|
||||||
export const restore = (backupId, password) => { };
|
export const restore = (backupId, password) => {
|
||||||
|
// return axios.get(ENDPOINT, {
|
||||||
|
// backupId,
|
||||||
|
// subHash: makeSubHash(password),
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
@ -47,6 +47,8 @@ module.exports = {
|
|||||||
CONF_SECTIONS: 'confSections',
|
CONF_SECTIONS: 'confSections',
|
||||||
PAGE_INFO: 'pageInfo',
|
PAGE_INFO: 'pageInfo',
|
||||||
APP_CONFIG: 'appConfig',
|
APP_CONFIG: 'appConfig',
|
||||||
|
BACKUP_ID: 'backupId',
|
||||||
|
BACKUP_HASH: 'backupHash',
|
||||||
},
|
},
|
||||||
topLevelConfKeys: {
|
topLevelConfKeys: {
|
||||||
PAGE_INFO: 'pageInfo',
|
PAGE_INFO: 'pageInfo',
|
||||||
@ -55,7 +57,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
toastedOptions: {
|
toastedOptions: {
|
||||||
position: 'bottom-center',
|
position: 'bottom-center',
|
||||||
duration: 2000,
|
duration: 2500,
|
||||||
keepOnHover: true,
|
keepOnHover: true,
|
||||||
className: 'toast-message',
|
className: 'toast-message',
|
||||||
iconPack: 'fontawesome',
|
iconPack: 'fontawesome',
|
||||||
|
17
yarn.lock
17
yarn.lock
@ -1904,6 +1904,13 @@ aws4@^1.8.0:
|
|||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||||
|
|
||||||
|
axios@^0.21.1:
|
||||||
|
version "0.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
babel-eslint@^10.0.1:
|
babel-eslint@^10.0.1:
|
||||||
version "10.1.0"
|
version "10.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
|
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
|
||||||
@ -2956,6 +2963,11 @@ crypto-browserify@^3.11.0:
|
|||||||
randombytes "^2.0.0"
|
randombytes "^2.0.0"
|
||||||
randomfill "^1.0.3"
|
randomfill "^1.0.3"
|
||||||
|
|
||||||
|
crypto-js@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc"
|
||||||
|
integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==
|
||||||
|
|
||||||
css-color-names@0.0.4, css-color-names@^0.0.4:
|
css-color-names@0.0.4, css-color-names@^0.0.4:
|
||||||
version "0.0.4"
|
version "0.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
|
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
|
||||||
@ -4167,6 +4179,11 @@ follow-redirects@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||||
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
|
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
|
||||||
|
|
||||||
|
follow-redirects@^1.10.0:
|
||||||
|
version "1.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||||
|
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
|
Loading…
Reference in New Issue
Block a user